《Java2实用教程第4版第12章Java多线程机制.ppt》由会员分享,可在线阅读,更多相关《Java2实用教程第4版第12章Java多线程机制.ppt(28页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、注意:开始用功了注意:开始用功了!Java2实用教程实用教程(第第4版版)第第12章章配合例子源代码一起使用例子源代码一起使用Power point 制作:耿祥义 张跃平JavaJava多线程机制多线程机制 导读导读主要内容主要内容Java中的线程中的线程Thread类与线程的创建类与线程的创建线程的常用方法线程的常用方法线程同步线程同步协调同步的线程协调同步的线程线程联合线程联合GUI线程线程计时器线程计时器线程12.1 进程与线程进程与线程 12.1.1 操作系统与进程操作系统与进程 程程序序是是一一段段静静态态的的代代码码,它它是应用软件执行的蓝本。是应用软件执行的蓝本。进进程程是是程程
2、序序的的一一次次动动态态执执行行过过程程,它它对对应应了了从从代代码码加加载载、执执行行至至执执行行完完毕毕的的一一个个完完整整过过程程,这这个个过过程程也也是是进进程程本本身身从从产产生生、发展至消亡的过程。发展至消亡的过程。现现代代操操作作系系统统可可以以同同时时管管理理一一个个计计算算机机系系统统中中的的多多个个进进程程,即即可可以以让让计计算算机机系系统统中中的的多多个个进进程轮流使用程轮流使用CPUCPU资源。资源。12.1.2 进程与线程进程与线程 线线程程是是比比进进程程更更小小的的执执行行单单位位,一一个个进进程程在在其其执执行行过过程程中中,可可以以产产生生多多个个线线程程,
3、形形成成多多条条执执行行线线索索,每每条条线线索索,即即每每个个线线程程也也有有它它自自身身的的产产生生、存存在在和消亡的过程。和消亡的过程。线线程程间间可可以以共共享享进进程程中中的的某某些些内内存存单单元元(包包括括代代码码与与数数据据),线线程程的的中中断断与与恢恢复复可可以以更更加加节省系统的开销。节省系统的开销。12.2 Java中的线程中的线程 12.2.1 Java的多线程机制的多线程机制 JavaJava语语言的一大特性点就是内置言的一大特性点就是内置对对多多线线程的支持。程的支持。JavaJava虚虚拟拟机机快快速速地地把把控控制制从从一一个个线线程程切切换换到到另另一一个个
4、线线程程。这这些些线线程程将将被被轮轮流流执执行行,使使得得每每个个线线程程都都有有机机会使用会使用CPUCPU资源。资源。12.2.2 主主线程(线程(mainmain线程)线程)每个每个Java应用程序都有一个缺省的主线程。应用程序都有一个缺省的主线程。当当JVM(Java Virtual Machine 虚虚拟拟机机)加加载载代代码码,发发现现main方方法法之之后后,就就会会启启动动一一个个线线程程,这这个个线线程程称称为为“主主线线程程”(main线程),该线程负责执行线程),该线程负责执行main方法。方法。JVM一直要等到一直要等到Java应用程序中的所有线程都结束之后,才应用程
5、序中的所有线程都结束之后,才结束结束Java应用程序应用程序。12.2.3 线程的状态与生命周期线程的状态与生命周期 建建的的线线程程在在它它的的一一个个完完整整的的生生命命周周期期中中通通常常要要经经历历如如下下的的四四种种状态:状态:1新新建建:当当一一个个Thread类类或或其其子子类类的的对对象象被被声声明明并并创创建建时时,新新生生的的线线程程对对象象处于新建状态。处于新建状态。2运运行行:线线程程必必须须调调用用startstart()方方法法(从从父父类类继继承承的的方方法法)通通知知JVMJVM,这这样样JVMJVM就就会会知知道道又又有有一一个个新新一一个个线线程程排排队队等
6、等候候切切换换了了。一一旦旦轮轮到到它它来来享享用用CPUCPU资资源时,此线程的就可以脱离创建它的主线程独立开始自己的生命周期了。源时,此线程的就可以脱离创建它的主线程独立开始自己的生命周期了。3中断中断:有有4 4种原因的中断:种原因的中断:JVMJVM将将CPUCPU资资源源从从当当前前线线程程切切换换给给其其他他线线程程,使使本本线线程程让让出出CPUCPU的的使使用用权权处于中断状态。处于中断状态。线线程程使使用用CPUCPU资资源源期期间间,执执行行了了sleep(int sleep(int millsecond)millsecond)方方法法,使使当当前前线线程进入休眠状。程进入
7、休眠状。线程使用线程使用CPUCPU资源期间,执行了资源期间,执行了wait()wait()方法。方法。线程使用线程使用CPUCPU资源期间,执行某个操作进入阻塞状态。资源期间,执行某个操作进入阻塞状态。4死亡死亡:处于死亡状态的线程不具有继续运行的能力。线程释放了实体。处于死亡状态的线程不具有继续运行的能力。线程释放了实体。例例子子1(Example12_1.java)通过分析运行结果阐述线程的4种状态。例子1在 主 线 程 中 用 Thread的 子 类 创 建 了 两 个 线 程(SpeakElephant.java,SpeakCar.java),这两个线程分别在命令行窗口输出20句“大
8、象”和“轿车”;主线程在命令行窗口输出15句“主人”。例子例子1的运行效果如图12.4。例例子子1在不同的计算机运行或在同一台计算机反复运行的结果不尽相同,输出结果依赖当前CPU资源的使用情况。12.2.4 线程调度与优先级线程调度与优先级 处处于于就就绪绪状状态态的的线线程程首首先先进进入入就就绪绪队队列列排排队队等等候候CPU资资源源,同同一一时时刻刻在在就就绪绪队队列列中中的的线线程程可可能能有有多多个个。Java虚虚拟拟机机(JVM)中中的的线线程程调调度度器器负负责责管管理理线线程程,调调度度器器把把线线程程的的优优先先级级分分为为10个个级级别别,分分别别用用Thread类类中中的
9、类常量表示。的类常量表示。Java调调度度器器的的任任务务是是使使高高优优先先级级的的线线程程能能始始终终运运行行,一一旦旦时时间间片片有有空空闲闲,则则使使具具有有同同等等优优先先级级的的线线程程以以轮轮流流的方式顺序使用时间片。的方式顺序使用时间片。12.3 ThreadThread类与线程的创建类与线程的创建 12.3.1 使用使用Thread的子类的子类 在在JavaJava语言中,用语言中,用ThreadThread类或子类创建线程对象。类或子类创建线程对象。在在编编写写ThreadThread类类的的子子类类时时,需需要要重重写写父父类类的的run()run()方方法法,其其目目的
10、的是是规规定定线线程程的的具具体体操操作作,否否则则线线程程就就什什么也不做,因为父类的么也不做,因为父类的run()run()方法中没有任何操作语句。方法中没有任何操作语句。12.3.2 使用使用ThreadThread类类 创建线程的另一个途径就是用Thread类直接创建线程对象。使用Thread创建线程通常使用的构造方法是:Thread(Runnable target)该构造方法中的参数是一个Runnable类型的接口。在创建线程对象时必须向构造方法的参数传递一个实现Runnable接口类的实例,该实例对象称作所创线程的目标对象,当线程调用start()方法后,一旦轮到它来享用CPU资源
11、,目标对象就会自动调用接口中的run()方法(接口回调)。例子例子2 (Example12_2.java,ElephantTarget.java,CarTarget.java)和前面的例例子子1不同,不使用Thread类的子类创建线程,而是使用Thread类创建speakElephant和speakCar线程,请读者注意比较例子1和例子2的细微差别。线程间可以共享相同的内存单元(包括代码与数据),并利用这些共享单元来实现数据交换、实时通信与必要的同步操作。例子例子3(Example12_3.java,House.java)中使用Thread类创建两个模拟猫和狗的线程,猫和狗共享房屋中的一桶水,
12、即房屋是线程的目标对象,房屋中的一桶水被猫和狗共享。猫和狗轮流喝水(狗喝的多,猫喝的少),当水被喝尽时,猫和狗进入死亡状态。猫或狗在轮流喝水的过程中,主动休息片刻(让Thread类调用sleep(int n)进入中断状态),而不是等到被强制中断喝水。12.3.3 目标对象与线程的关系目标对象与线程的关系 从对象和对象之间的关系角度上看,目标对象和线程的关系有以下两种情景。1.目标对象和线程完全解耦 目标对象没有组合线程对象.目标对象经常需要通过获得线程的名字(因为无法获得线程对象的引用)以便确定是哪个线程正在占用CPU资源,即被JVM正在执行的线程。2.目标对象组合线程(弱耦合)目标对象可以组
13、合线程.目标对象类组合线程对象时,目标对象可以通过获得线程对象的引用.例子例子4中中(Example12_4.java Example12_4.java,House.java House.java),线程cat和dog在House中,请注意例子4与例子3的区别.12.3.4 关于关于run()run()方法启动的次数方法启动的次数 对对于于具具有有相相同同目目标标对对象象的的线线程程,当当其其中中一一个个线线程程享享用用CPU资资源源时时,目目标标对对象象自自动动调调用用接接口口中中的的run方方法法,这这时时,run方方法法中中的的局局部部变变量量被被分分配配内内存存空空间间,当当轮轮到到另
14、另一一个个线线程程享享用用CPU资资源源时时,目目标标对对象象会会再再次次调调用用接接口口中中的的run方方法法,那那么么,run()方方法法中中的的局局部部变变量量会会再再次次分分配配内内存存空空间间。也也就就是是说说run()run()方方法法已已经经启启动动运运行行了了两两次次,分别运行在不同的线程中,即运行在不同的时间片内。分别运行在不同的线程中,即运行在不同的时间片内。12.4 线程的常用方法线程的常用方法 1start():线程调用该方法将启动线程,使之从新建状态进入就绪线程调用该方法将启动线程,使之从新建状态进入就绪队列排队,一旦轮到它来享用队列排队,一旦轮到它来享用CPUCPU
15、资源时,就可以脱离创建它的线程独立开资源时,就可以脱离创建它的线程独立开始自己的生命周期了。始自己的生命周期了。2run():Thread类类的的run()方方法法与与Runnable接接口口中中的的run()方方法法的的功功能能和和作作用用相相同同,都都用用来来定定义义线线程程对对象象被被调调度度之之后后所所执执行行的的操操作作,都都是是系系统统自自动调用而用户程序不得引用的方法。动调用而用户程序不得引用的方法。3sleep(int millsecond):优先级高的线程可以在它的优先级高的线程可以在它的run()方法中调方法中调用用sleep方法来使自己放弃方法来使自己放弃CPU资源,休眠
16、一段时间。资源,休眠一段时间。4isAlive():线程处于线程处于“新建新建”状态时,线程调用状态时,线程调用isAlive()方法返回方法返回false。在线程的在线程的run()方法结束之前,即没有进入死亡状态之前,线程调用方法结束之前,即没有进入死亡状态之前,线程调用isAlive()方法返回方法返回true。5currentThread():该方法是该方法是Thread类中的类方法,可以用类名调类中的类方法,可以用类名调用,该方法返回当前正在使用用,该方法返回当前正在使用CPU资源的线程。资源的线程。6interrupt():一个占有一个占有CPU资源的线程可以让休眠的线程调用资源的
17、线程可以让休眠的线程调用interrupt()方法方法“吵醒吵醒”自己,即导致休眠的线程发生自己,即导致休眠的线程发生InterruptedException异常,异常,从而结束休眠,重新排队等待从而结束休眠,重新排队等待CPU资源。资源。例子例子5(Example12_5.java,Home.java)中一个线程每隔1秒钟在命令行窗口输出本地机器的时间,在3秒钟后,该线程又被秒钟后,该线程又被分配了实体分配了实体,新实体又开始运行。新实体又开始运行。因为垃圾实体仍然在工作垃圾实体仍然在工作,因此,在命令行每秒钟能看见两行同样的本地机器时间.运行效果如图12.7。例例子子6(Example12
18、_6.java,ClassRoom.java)中,有两个线程:student和teacher,其中student准备睡一小时后再开始上课,teacher在输出3句“上课”后,吵醒休眠的线程student。运行效果如图12.8。12.5 线程同步线程同步 在处理多线程问题时,我们必须注意这样一个问题:当两个或多个线程同时访问同一个变量,并且一个线程需要修改这个变量。我们应对这样的问题作出处理。在处理线程同步时,要做的第一件事就是要把修改数据的方法用用关关键键字字synchronizedsynchronized来修饰。来修饰。所谓线程同步所谓线程同步就是若干个线程都需要使用一个synchroniz
19、ed修饰的方法。例例子子7(7(Example12_7.javaExample12_7.java ,Bank.java Bank.java)中有两个线程:会计和出纳,他俩共同拥有一个帐本.程序要保证其中一人使用saveOrTake(int amount)时,另 一 个 人 将 必 须 等 待,即 saveOrTake(int amount)方 法 应 当 是 一 个synchronizedsynchronized方法方法。程序运行效果如图12.9.12.6 协调同步的线程协调同步的线程 wait()wait()方方法法可可以以中中断断方方法法的的执执行行,使使本本线线程程等等待待,暂暂时时让让
20、出出CPUCPU的的使使用用权,并允许其它线程使用这个同步方法。权,并允许其它线程使用这个同步方法。notifyAll()notifyAll()方方法法通通知知所所有有的的由由于于使使用用这这个个同同步步方方法法而而处处于于等等待待的的线线程程结结束束等等待待。曾曾中中断断的的线线程程就就会会从从刚刚才才的的中中断断处处继继续续执执行行这这个个同同步步方方法法,并并遵遵循循“先中断先继续先中断先继续”的原则。的原则。notify()notify()方法方法只是通知处于等待中的线程的某一个结束等待。只是通知处于等待中的线程的某一个结束等待。例子例子8(Example12_8.java,Ticke
21、tHouse.java)模拟两个人,模拟两个人,张飞和李逵买电影票。售票员张飞和李逵买电影票。售票员只有只有两张五元的钱两张五元的钱,电影票,电影票5元元钱一张。钱一张。张飞拿二十元一张的张飞拿二十元一张的人民币人民币排在李逵的前面买票,排在李逵的前面买票,李逵拿一张李逵拿一张5元的人民币买票元的人民币买票。因此因此张飞必须等待张飞必须等待(李逵比张(李逵比张飞先买了票)。程序运行效果飞先买了票)。程序运行效果如图如图12.10。12.7 线程联合线程联合 一个线程A在占有CPU资源期间,可以让其它线程调用join()和本线程联合,如:B.join();B.join();称A在运行期间联合了B
22、。如果线程A在占有CPU资源期间一旦联合B线程,那么A线程将立刻中断执行,一直等到它联合的线程B执行完毕,A线程再重新排队等待CPU资源,以便恢复执行。如果A准备联合的B线程已经结束,那么B.join()不会产生任何效果。例例子子9(9(Example12_9.javaExample12_9.java ,ThreadJoin.javaThreadJoin.java )使用线程联合模拟顾客等待蛋糕师制作蛋糕,程序运行效果如图12.11.12.8 GUIGUI线程线程 当Java程序包含图形用户界面(GUI)时,Java虚拟机在运行应用程序时会自动启动更多的线程,其中有两个重要的线程:AWT-Ev
23、entQuecueAWT-EventQuecue和AWT-WindowsAWT-Windows。AWT-EventQuecue线程负责处理GUI事件,AWT-Windows线程负责将窗体或组件绘制到桌面。JVM要保证各个线程都有使用CPU资源的机会,比如,程序中发生GUI界面事件时,JVM就会将CPU资源切换给AWT-EventQuecue线程,AWT-EventQuecue线程就会来处理这个事件,比如,你单 击 了 程 序 中 的 按 钮,触 发 ActionEvent事 件,AWT-EventQuecue线程就立刻排队等候执行处理事件的代码 例例子子10(Example12_10.java
24、,WindowTyped.java)是训练用户寻找键盘上的字母的快速能力。一个线程giveLetter负责每隔3秒给出一个英文字母,用户需要在文本框中输入这个英文字母,按回车确认。当用户按回车键时,将触发ActionEvent事件,那么JVM就 会 中 断 giveLetter线 程,把 CUP的 使 用 权 切 换 给 WT-EventQuecue线程,以便处理ActionEvent事件。程序运行效果如图12.12。例例子子11(Example12_11.java,Win.java)中单击start按扭线程开始工作:每隔一秒钟显示一次当前时间;单击stop按扭后,线程就结束了生命,释放了实体
25、,即释放线程对象的内存.把一个线程委派给一个组件事件时要格外小心,比如单击一个按扭让线程开始运行,那么当这个线程在执行完run()方法之前,客户可 能 会 随 时 再 次 单 击 该 按 扭,这 时 就 会 发 生ILLegalThreadStateException 异常。程序运行效果如图12.13.当某些操作需要周期性地执行,就可以使用计时器。我们可以使用Timer类的构造方法:Timer(int a,Object b)创建一个计时器,其中的参数a的单位是豪秒,确定计时器每隔a 毫秒“震铃”一次,参参数数b是是计计时时器器的的监监视视器器。计时器发发生生的的震震铃铃事事件件是ActinEv
26、ent类型事件。当震铃事件发生时,监视器就会监 视 到 这 个 事 件,监监 视视 器器 就就 回回 调调 ActionListener接接 口口 中中 的的actionPerformed(ActionEvent e)方方法法。使用Timer类的方法start()启动计时器,即启动线程。使用Timer类的方法stop()停止计时器,即挂起线程,使用restart()重新启动计时器,即恢复线程.12.9 计时器线程计时器线程 例例子子12(Example12_12.java,WindowTime.java )中,单击“开始”按钮启动计时器,并将时间显示在文本框中,同时移动文本框在容器中的位置;单
27、击“暂停”按钮暂停计时器;单击“继续”按钮重新启动计时器。程序运行效果如图12.14。一个线程调用void setDaemon(boolean on)方法可以将自己设置成一个守护(Daemon)线程,例如:thread.setDaemon(true);当程序中的所有用户线程都已结束运行时,即使守护线程的run方法中还有需要执行的语句,守护线程也立刻结束运行。例例子子13(13(Example12_13.javaExample12_13.java ,Daemon.java Daemon.java)中有一个守护线程 .12.10 守护线程守护线程 在在电电视视节节目目中中经经常常看看见见主主持持人
28、人提提出出的的问问题题,并并要要求求考考试试者者在在限限定定时时间间内内回回答答问问题题。这这里里由由程程序序提提出出问问题题,用用户户回回答答问问题题。问问题题保保存存在在test.txt中中,test.txt的格式如下:的格式如下:(1)每个问题每个问题提供提供A、B、C、D四个选择四个选择(单项选择)。(单项选择)。(2)两个问题之间是用减号(两个问题之间是用减号(-)尾加前一问题的答案)尾加前一问题的答案分隔(例如:分隔(例如:-D-)。)。例例题题1414(Example12_14.java Example12_14.java,StandardExamInTime.java StandardExamInTime.java)运运行行效效果果如如图图12.15。12.11 应用举例应用举例