第八章Java多线程机制本次课教学目标了解Java的多线程机制掌握创建线程的两个方法熟悉线程的常用方法掌握线程同步的意义Java的多线程机制多线程机制是Java语言的又一重要特征,使用多线程技术可以使系统同时运行多个执行体,加快程序的响应时间,提高计算机资源的利用率。使用多线程技术可以提高整个应用系统的性能。程序、进程与线程的区别:程序是一段静态的代码,它是应用软件执行的蓝本。进程是程序的一次动态执行过程,它对应了从代码加载、执行至执行完毕的一个完整过程,这个过程也是进程本身从产生、发展至消亡的过程。线程是比进程更小的执行单位,一个进程在其执行过程中,可以产生多个线程,形成多条执行线索,每条线索,即每个线程也有它自身的产生、存在和消亡的过程,也是一个动态的概念。多线程和多任务:多线程和多任务是两个既有联系又有区别的概念,多任务是针对操作系统而言的,代表着操作系统可以同时执行的程序个数;多线程是针对一个程序而言的,代表着一个程序内部可以同时执行的线程个数,而每个线程可以完成不同的任务。例如Java推出的HotJava浏览器,你可以一边浏览网页一边下载新网页,可以同时显示动画和播放音乐。主线程:当JVM加载代码,发现main方法之后,就会启动一个线程,这个线程称作“主线程”,该线程负责执行main方法。如果main方法中没有创建其他的线程,那么当main方法执行完最后一个语句,JVM就会结束我们的Java应用程序。如果main方法中又创建了其他线程,那么JVM就要在主线程和其他线程之间轮流切换,保证每个线程都有机会使用CPU资源,main方法即使执行完最后的语句,JVM也不会结束我们的程序,JVM一直要等到程序中的所有线程都结束之后,才结束我们的Java应用程序。线程的状态与生命周期新建当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态。此时它已经有了相应的内存空间和其他资源。运行线程创建之后就具备了运行的条件,一旦轮到它来享用CPU资源时,即JVM将CPU使用权切换给该线程时,此线程的就可以脱离创建它的主线程独立开始自己的生命周期了(即run方法执行的过程)。中断有4种原因的中断:CPU资源从当前线程切换给其他线程执行了sleep(intmillsecond)方法执行了wait()方法进入阻塞状态死亡run方法结束。要将一段代码放在一个新的线程上面执行,那么这段代码应该放在一个类的run方法中,并且这个类是Thread的子类,子类要覆盖Thread类中的run方法,写出想要在新的线程上面执行是代码启动一个新的线程,我们不是直接调用Thread的子类对象的run方法,而是调用Thread子类对象的start方法,该方法启动一个新的线程,并在该线程上面运行Thread对象的run方法。由于线程的代码段在run方法中,那么run方法执行结束后线程也就相应的结束了,因而我们可以通过控制run方法中的循环条件来控制线程的结束例1:classLefthandextendsThread{publicvoidrun(){for(inti=1;i=9;i++){System.out.println(我是左手线程);}}}classRighthandextendsThread{publicvoidrun(){for(inti=1;i=9;i++){System.out.println(我是右手线程);}}}publicclassExample{publicstaticvoidmain(Stringargs[]){Lefthandleft;Righthandright;left=newLefthand();//创建线程right=newRighthand();left.start();right.start();for(inti=1;i=6;i++){System.out.println(我是主线程);}}}classTestThread{publicvoidrun(){while(true){System.out.println(“run:”+Thread.currentThread.getNme());}}}classThreadDemo1{publicstaticvoidmain(String[]args){newTestThread.run();while(true){System.out.println(“main:”+Thread.currentThread().getName());}}}前台线程,后台线程,和联合线程如果我们对某个对象在启动之前调用了setDaemon(true)方法,这个线程就是后台线程,反之就是前台线程。对java程序来说,只要还有一个前台线程在运行,那么这个进程就不会结束,如果一个进程之中只有一个后台线程在运行,那么这个进程就会结束pp.join的作用就是把pp所对应的线程合并到调用pp.join()语句的线程之中线程的创建创建线程的方式有两种:通过创建Thread类的子类来实现;通过实现Runnable接口的类来实现。Thread的子类创建线程设计Thread的子类,重写父类的run方法用Thread类或子类创建线程对象使用start方法启动线程当JVM将CPU使用权切换给线程时,自动执行run方法。classThread1extendsThread{Strings;intm,count=0;Thread1(Stringss,intmm){s=ss;m=mm;}publicvoidrun(){try{while(true){System.out.print(s);sleep(m);count++;if(count=20)break;}System.out.println(s+finished!);}catch(InterruptedExceptione){return;}}publicstaticvoidmain(Stringargs[]){Thread1threadA=newThread1(A,50);Thread1threadB=newThread1(B,100);threadA.start();threadB.start();}}使用Runnable接口实现多线程用继承Thread类的子类或实现Runable接口的类来创建线程无本质区别,但由于Java不支持多重继承,所以如果一个类必须继承另一个非Thread类,此时要实现多线程只能通过实现Runnable接口的方式。通过Runnable接口实现多线程的方法:设计一个实现Runnable接口的类,重写run方法;以该类的对象为参数建立Thread类的对象;调用Thread类对象的start方法启动线程,将执行权转交到run方法。模拟铁路售票系统实现4个售票处,分别用4个线程来售票来出售某日某列车的100张票classTestThreadextendsThread{intticket=20;publicvoidrun(){while(true){if(ticket0){System.out.println(Thread.currentThread().getName()+issallingticket+ticket--);}}}}classThreadDemo{publicstaticvoidmain(String[]args){newTestThread().start();newTestThread().start();newTestThread().start();newTestThread().start();}}classThreadDemo{publicstaticvoidmain(String[]args){/*newTestThread().start();newTestThread().start();newTestThread().start();newTestThread().start();*/TestThreadtt=newTeatThread();tt.start();tt.start();tt.start();tt.start();}}classTestThread/*extendsThread*/implementsRunnable{intticket=20;publicvoidrun(){while(true){if(ticket0){System.out.println(Thread.currentThread().getName()+issallingticket+ticket--);}}}}classThreadDemo{publicstaticvoidmain(String[]args){/*newTestThread().start();newTestThread().start();newTestThread().start();newTestThread().start();*/TestThreadtt=newTeatThread();newThread(tt).start();newThread(tt).start();newThread(tt).start();newThread(tt).start();}}使用Runnable接口创建多线程适合多个相同的程序代码去处理同意资源的情况,把虚拟CPU(线程)同程序代码、数据有效分离,较好的体现的面向对象的设计思想事实上,几乎所有的多线程都可以用Runnable接口方式实现线程的常用方法start():线程调用该方法将启动线程,使之从新建状态进入就绪队列排队,一旦轮到它来享用CPU资源时,就可以脱离创建它的线程独立开始自己的生命周期了。run():线程对象被调度之后所执行的操作,由系统自动调用,用户程序不得引用。系统的Thread类中,run()方法没有具体内容,所以用户程序需要创建自己的Thread类的子类,并重写run()方法来覆盖原来的run()方法。当run方法执行完毕,线程就变成死亡状态。sleep(intmillsecond):线程占有CPU期间,执行sleep方法来使自己放弃CPU资源,休眠一段时间。如果线程在休眠时被打断,JVM就抛出InterruptedException异常。因此,必须在try~catch语句块中调用sleep方法。isAlive():线程处于运行状态时,isAlive()方法返回true,否则返回false。注意:一个已经运行的线程在没有进入死亡状态时,不要再给线程分配实体。currentThread():是Thread类中的类方法,可以用类名调用,该方法返回当前正在使用CPU资源的线程。interrupt():用来“吵醒”休眠的线程。多线程在实际生活中的应用网络聊天程序的收发单线程:如果一方从键盘上读取了数据并且发送给了对方,程序运行到“读取对方数据”,并一直等待对方发送数据,如果对方不回应那么程序将不再做任何事情,这是程序将处于阻塞状态,就不能正常的发送其他信息给用户了多线程在实际生活中的应用而是用多线程能够有效避免类似的事情发生线程同步Java提供了多线程机制,通过多线程的并发运行可以提高系统资源利用率,改善系统性能。但在有些情况下,一个线程必须和其他线程合作才能共同完成任务。线程可以共享内存,利用这个特点可以在线程之间传递信息。在Java中,实现同步操作的方法是在共享内存变量的方法前加synchronized修饰符。在程序运行过程中,如果某一线程调用经synchronized修饰的方法,在该线程结束此方法的运行之前,其他所有线程都不能运行该方法,只有等该线程完成此方法的运行后,其他线程才能y运行该方法。例5:线程并发引起的不确定性classTestThread/*extendsThread*/implementsRunnable{intticket=20;publicvoidru