一、概念1、进程1.1进程:是一个进行中的程序,每个进程执行都有一个执行顺序,也就是一个执行路径,或者说是一个控制单元。1.2线程:是进程中一个独立的控制单元。线程控制进程的执行。一个进程中至少有一个线程。1.3javaVM实例:JavaVM启动时,会有一个进程java.exe。这个进程中至少有一个线程负责运行java程序,这个线程运行的代码存在于main方法中。该线程称为main方法。线。扩展:其实更详细的解释一下jvm,jvm启动的线程不止一个,而且线程负责垃圾回收机制,继承Thread类1.1定义继承Thread的类,重写Thread类中的run方法来存储run方法中的自定义代码,让线程运行1.2调用线程的start方法,这个方法有两个作用:启动线程,调用run方法1.3多线程运行时,每次运行结果都不一样,因为多个线程都获得了CPU的执行权,CPU执行谁就跑谁。需要明确的是,在某一时刻,只能运行一个程序。(多核除外),cpu在快速切换,达到好像同时在运行的效果。我们可以直观地将多线程的运行行为放在cpu的执行权中。这是多线程的一个特点,随机性。谁抢到谁就执行。至于执行多久,CPU说了算。publicclassDemoextendsThread{publicvoidrun(){for(intx=0;x<60;x++){System.out.println(this.getName()+"demorun---"+x);}}publicstaticvoidmain(String[]args){Demod=newDemo();//创建线程d.start();//启动线程并执行线程的run方法d.run();//对象调用该方法,线程为createdbutnotDidnotrunfor(intx=0;x<60;x++){System.out.println("HelloWorld---"+x);}}}2创建多线程的第二种方式,步骤:2.1定义类实现Runnable接口2.2重写了Runnable接口中的run方法:将线程要运行的代码存放在run方法2.3中。通过Thread类创建线程对象2.4。将Runnable接口的子类对象作为实参传递给Thread类的构造函数为什么要将Runnable接口的子类对象传递给Thread的构造函数:因为自定义run方法所属的对象是Thread类的子类对象Runnable接口,所以为了让线程执行指定对象的run方法,必须指定run方法2.5。调用Thread类的start方法启动线程,调用Runnable接口子类的方法/**需求:简单的购票程序,多个窗口同时售票*/publicclassTicketimplementsRunnable{privatestaticinttick=100;Objectobj=newObject();booleanflag=true;publicvoidrun(){if(flag){while(true){synchronized(Ticket.class){if(tick>0){System.out.println(Thread.currentThread()。getName()+"code:"+tick--);}}}}else{while(true){show();}}}publicstaticsynchronizedvoidshow(){if(tick>0){System.out.println(Thread.currentThread().getName()+"show:"+tick--);}}}classThisLockDemo{publicstaticvoidmain(String[]args){Tickett=newTicket();Threadt1=newThread(t);try{Thread.sleep(10);}catch(Exceptione){//TODO:handleexception}t.flag=false;Threadt2=newThread(t);//Threadt3=newThread(t);//Threadt4=newThread(t);t1.start();t2.start();//t3.start();//t4.start();}}3.实现和继承有什么区别3.1该实现方法避免了单继承的限制。推荐在定义线程时使用实现方法。3.2.继承Thread类:线程代码存放在Thread子类run方法中3.3.实现Runnable:线程代码存放在接口Medium的子类run方法中4.多线程的特点-运行和启动4.1为什么要重写run方法:Thread类是用来描述线程的,它定义了一个函数,用于存放线程要运行的代码,存储函数是run方法,也就是Thread类中的run方法,用来存储线程要运行的代码5.多线程运行状态创建线程-run---sleep()/wait()--freeze---notify()--唤醒创建线程-run---stop()-die创建线程-run-没有抢cpu执行右-临时冻结6.获取线程对象及其名称6.1。线程有自己的默认名称,编号从0开始6.2.staticThreadcurrentThread():获取当前线程对象6.3.getName():获取线程名称6.4.设置线程名称:setName()或使用构造函数publicclassTestextendsThread{Test(Stringname){super(name);}publicvoidrun(){for(intx=0;x<60;x++){System.out.println((Thread.currentThread()==this)+"..."+this.getName()+"run..."+x);}}}classThreadTest{publicstaticvoidmain(String[]args){Testt1=newTest("one---");Testt2=newTest("two+++");t1.start();t2.start();t1.run();t2.run();for(intx=0;x<60;x++){System.out.println("main----"+x);}}}三、多线程安全问题1、多线程安全问题产生的原因:1.1.当多条语句在同一个线程操作共享数据时,一个线程只执行了多条语句的一部分,另一线程在执行完成之前就参与了执行,导致共享数据错误。只能执行一个线程。在执行过程中,其他线程不能参与执行。1.3.Java针对多线程安全问题提供了专业的解决方案,就是同步代码块:Synchronized(object){需要同步的代码},对象就像一把锁,持有锁的线程可以同步执行,而没有锁的线程即使获得了cpu执行权也无法进入,因为没有获得锁2、同步前提:2.1.必须有2个或2个以上的线程2.2。多个线程必须使用同一个锁2.3。好处是解决了多线程2.4的安全问题。缺点是需要多线程判断锁,比较耗资源2.5。synchronizationfunction定义同步函数,方法中可以用synchronized/**修改钱要求:*银行有一个金库,两个存款人每人存300元,每次100元,共存3次*目的:是否程序有安全问题,如果有,如何解决*如何发现问题:*1.指定哪些代码是多线程代码*2。指定共享数据*3。指定多线程代码中哪些语句对共享数据进行操作*/publicclassBank{privateintsum;Objectobj=newObject();//定义同步函数,在方法中修改为synchronizedpublicsynchronizedvoidadd(intn){//synchronized(obj){总和=sum+n;try{Thread.sleep(10);}catch(InterruptedExceptione){//TODO自动生成的catchblocke.printStackTrace();}System.out.println("sum="+sum);//}}}classCusimplementsRunnable{privateBankb=newBank();publicvoidrun(){for(intx=0;x<3;x++){b.add(100);}}}classBankDemo{publicstaticvoidmain(String[]args){Cusc=newCus();Threadt1=newThread(c);Threadt2=newThread(c);t1.start();t2.start();}}6.同步锁6.1函数需要被对象调用,所以函数有对象引用,也就是this.,所以同步函数使用的锁是this6.2.静态函数的锁是类对象静态加载到内存时,内存中没有这个类的对象,但是必须有类对应的字节码文件对象,类名.class,类型对象是Class6.3。静态同步方法,使用的锁是方法所在类的字节码文件对象,类名。卖票*/publicclassTicketimplementsRunnable{privatestaticinttick=100;Objectobj=newObject();booleanflag=true;publicvoidrun(){if(flag){while(true){synchronized(Ticket.class){if(tick>0){系统。out.println(Thread.currentThread().getName()+"code:"+tick--);}}}}else{while(true){show();}}}publicstaticsynchronizedvoidshow(){if(tick>0){System.out.println(Thread.ccurrentThread().getName()+"show:"+tick--);}}}classThisLockDemo{publicstaticvoidmain(String[]args){Tickett=newTicket();Threadt1=newThread(t);try{Thread.sleep(10);}catch(Exceptione){//TODO:handleexception}t.flag=false;Threadt2=newThread(t);//Threadt3=newThread(t);//Threadt4=newThread(t);t1.start();t2.start();//t3.start();//t4.start();}}7.多线程,单例模式-lazy风格lazy风格和hungry风格的区别:lazy风格可以延迟Instance加载,如果多线程访问,lazy风格会存在安全问题,可以使用同步来解决。同步函数和同步代码都可以使用,但是效率相对较低。双重判断可以解决效率低下的问题,加上同步时使用的锁是该类型锁的字节码文件对象/**单例模式*///饿汉式publicclassSingle{privatestaticfinalSingles=newSingle();privateSingle(){}publicstaticSinglegetInstance(){returns;}}//LazyclassSingle2{privatestaticSingle2s2=null;privateSingle2(){}publicstaticSingle2getInstance(){if(s2==null){synchronized(Single2.class){if(s2==null){s2=newSingle2();}}}returns2;}}classSingleDemo{publicstaticvoidmain(String[]args){System.out.println("HelloWorld");}}8.多线程死锁同步会导致嵌套同步死锁/**需求:简单的购票程序,多个窗口同时售票*/publicclassDeadTestimplementsRunnable{privatebooleanflag;DeadTest(booleanflag){this.flag=flag;}publicvoidrun(){if(flag){synchronized(MyLock.locka){System.out.println("iflocka");synchronized(MyLock.lockb){System.out.println("iflockb");}}}else{同步(MyLock.lockb){System.out.println("elselockb");同步(MyLock.locka){System.out.println("elselocka");}}}}}classMyLock{staticObjectlocka=newObject();staticObjectlockb=newObject();}classDeadLockDemo{publicstaticvoidmain(String[]args){Threadt1=newThread(newDeadTest(true));Threadt2=newThread(newDeadTest(false));t1.start();t2.start();}}
