Java语言程序设计实验指导电子教案第08章.pptx
-
资源ID:80068587
资源大小:147.09KB
全文页数:24页
- 资源格式: PPTX
下载积分:20金币
快捷下载
会员登录下载
微信登录下载
三方登录下载:
微信扫一扫登录
友情提示
2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
|
Java语言程序设计实验指导电子教案第08章.pptx
8.1 8.1 多线程编程概述多线程编程概述 本节介绍多线程编程的基础知识,包括多线程本节介绍多线程编程的基础知识,包括多线程的基本概念、的基本概念、JavaJava的线程模型(线程优先级、同步的线程模型(线程优先级、同步性、消息传递)等方面的内容。性、消息传递)等方面的内容。8.1.1 8.1.1 什么是多线程什么是多线程8.1.2 Java8.1.2 Java线程模型线程模型Return第1页/共24页8.1.1 8.1.1 什么是多线程什么是多线程 同同 其其 他他 大大 多多 数数 编编 程程 语语 言言 不不 同同,JavaJava内内 置置 支支 持持 多多 线线 程程 编编 程程(multithreaded multithreaded programmingprogramming)。多多线线程程程程序序包包含含两两条条或或两两条条以以上上并并发发运运行行的的部部分分,把把程程序序中中每每个个这这样样的的部部分分都都叫叫作作一一个个线线程程(threadthread)。每每个个线线程程都都有有独独立立的的执执行行路路径径,因因此此多多线线程程是是多任务处理的一种特殊形式。多任务处理的一种特殊形式。读读者者可可能能知知道道多多任任务务处处理理,它它实实际际上上被被所所有有的的现现代代操操作作系系统统所所支支持持。然然而而,多多任任务务处处理理有有两两种种截截然然不不同同的的类类型型:基基于于进进程程的的和和基基于于线线程程的的。搞搞清清楚楚两两者者的的区区别别是是很很重重要要的的。对对大大多多数数读读者者来来说说,基基于于进进程程的的多多任任务务处处理理是是更更熟熟悉悉的的形形式式。进进程程(processprocess)本本质质上上是是一一个个执执行行的的程程序序。因因此此基基于于进进程程的的多多任任务务处处理理的的特特点点是是允允许许你你的的计计算算机机同同时时运运行行两两个个或或更更多多的的程程序序。举举例例来来说说,基基于于进进程程的的多多任任务务处处理理使使你你在在运运用用文文本本编编辑辑器器的的时时候候可可以以同同时时运运行行JavaJava编编译译器器。在在基基于于进进程程的的多多任任务务处处理理中中,程程序序是是调调度度程程序序所所分分派派的的最最小小代码单位。代码单位。而而在在基基于于线线程程(thread-basedthread-based)的的多多任任务务处处理理环环境境中中,线线程程是是最最小小的的执执行行单单位位。这这意意味味着着一一个个程程序序可可以以同同时时执执行行两两个个或或者者多多个个任任务务的的功功能能。例例如如,一一个个文文本本编编辑辑器器可可以以在在打打印印的的同同时时格格式式化化文文本本。所所以以,多多进进程程程程序序处处理理“大大图图片片”,而而多多线线程程程程序序处处理理细细节问题。节问题。Return 多线程程序比多进程程序需要更少的管理费用。进程是重量级的任务,需要分配给它们独立的地址空间。进程间通信是昂贵和受限的。进程间的转换也是很需要花费的。另一方面,线程是轻量级的选手。它们共享相同的地址空间并且共同分享同一个进程。线程间通信是便宜的,线程间的转换也是低成本的。当Java程序使用多进程任务处理环境时,多进程程序不受Java的控制,而多线程则受Java控制。多线程可帮助你编写出CPU最大利用率的高效程序,使得空闲时间保持最低。这对Java运行的交互式的网络互连环境是至关重要的,因为空闲时间是公共的。例如,网络的数据传输速率远低于计算机处理能力,而本地文件系统资源的读写速度也远低于CPU的处理能力。当然,用户输入也比计算机慢很多。在传统的单线程环境中,程序必须等待每一个这样的任务完成以后才能执行下一步尽管CPU有很多空闲时间。多线程使你能够获得并充分利用这些空闲时间。第2页/共24页8.1.2 Java8.1.2 Java线程模型线程模型 Java Java运行系统在很多方面依赖于线程,所有的类库设运行系统在很多方面依赖于线程,所有的类库设计都考虑到多线程。实际上,计都考虑到多线程。实际上,JavaJava使用线程来使整个环境使用线程来使整个环境异步。这有利于通过防止异步。这有利于通过防止CPUCPU循环的浪费来减少无效部分。循环的浪费来减少无效部分。为为更更好好地地理理解解多多线线程程环环境境的的优优势势,我我们们可可以以将将它它与与它它的的对对照照物物相相比比较较。单单线线程程系系统统的的处处理理途途径径是是使使用用一一种种叫叫作作轮轮询询的的事事件件循循环环方方法法。在在该该模模型型中中,单单线线程程控控制制在在一一无无限限循循环环中中运运行行,轮轮询询一一个个事事件件序序列列来来决决定定下下一一步步做做什什么么。一一旦旦轮轮询询装装置置返返回回信信号号表表明明已已准准备备好好读读取取网网络络文文件件,事事件件循循环环调调度度控控制制管管理理到到适适当当的的事事件件处处理理程程序序。直直到到事事件件处处理理程程序序返返回回,系系统统中中没没有有其其他他事事件件发发生生。这这就就浪浪费费了了CPUCPU时时间间。这这导导致致了了程程序序的的一一部部分分独独占占了了系系统统,阻阻止止了了其其他他事事件件的的执执行行。总总的的来来说说,单单线线程程环环境境,当当一一个个线线程程因因为为等等待待资资源源时时阻塞(阻塞(blockblock,挂起执行),整个程序停止运行。,挂起执行),整个程序停止运行。Java多线程的优点就在于取消了主循环/轮询机制。一个线程可以暂停而不影响程序的其他部分。例如,当一个线程从网络读取数据或等待用户输入时产生的空闲时间可以被利用到其他地方。多线程允许活的循环在每一帧间隙中沉睡一秒而不暂停整个系统。在Java程序中出现线程阻塞,仅有一个线程暂停,其他线程继续运行。线程存在多种状态。线程可以正在运行(running),只要获得了CPU时间它就可以运行;运行的线程可以被挂起(suspend),并临时中断它的执行;一个挂起的线程可以被恢复(resume),允许它从停止的地方继续运行;一个线程可以在等待资源时被阻塞(block);在任何时候,线程可以被终止(terminate),这将立即中断运行。一旦终止,线程不能被恢复。线程的各状态间关系见教材P190页图8-1所示。第3页/共24页下面简要介绍与下面简要介绍与JavaJava线程相关的几个概念线程相关的几个概念 JavaJava给给每每个个线线程程安安排排优优先先级级以以决决定定与与其其他他线线程程比比较较时时该该如如何何对对待待该该线线程程。线线程程优优先先级级是是详详细细说说明明线线程程间间优优先先关关系系的的整整数数。作作为为绝绝对对值值,优优先先级级是是毫毫无无意意义义的的;当当只只有有一一个个线线程程时时,优优先先级级高高的的线线程程并并不不比比优优先先级级低低的的线线程程运运行行的的快快。相相反反,线线程程的的优优先先级级是是用用来来决决定定何何时时从从一一个个运运 行行 的的 线线 程程 切切 换换 到到 另另 一一 个个。这这 叫叫“上上 下下 文文 转转 换换”(context context switchswitch)。决定上下文转换发生的规则很简单:)。决定上下文转换发生的规则很简单:l l线线程程可可以以自自动动放放弃弃控控制制。在在I/OI/O未未决决定定的的情情况况下下,睡睡眠眠或或阻阻塞塞由由明明确确的的让让步步来来完完成成。在在这这种种假假定定下下,所所有有其其他他的的线线程程被被检检测测,准准备运行的最高优先级线程被授予备运行的最高优先级线程被授予CPUCPU。l l线线程程可可以以被被高高优优先先级级的的线线程程抢抢占占。在在这这种种情情况况下下,低低优优先先级级线线程程不不主主动动放放弃弃,处处理理器器只只是是被被先先占占无无论论它它正正在在干干什什么么处处理理器器被被高高优优先先级级的的线线程程占占据据。基基本本上上,一一旦旦高高优优先先级级线线程程要要运运行行,它它就就执行。这叫做有优先级的多任务处理。执行。这叫做有优先级的多任务处理。当两个相同优先级的线程竞争当两个相同优先级的线程竞争CPUCPU周期时,情形有一点复杂。对周期时,情形有一点复杂。对于于WindowsWindows这样的操作系统,等优先级的线程是在循环模式下自动划这样的操作系统,等优先级的线程是在循环模式下自动划分时间的。对于其他一些非分时间的。对于其他一些非WindowsWindows操作系统而,如操作系统而,如Solaris 2.xSolaris 2.x,等优先级线程相对于它们的对等体自动放弃。如果不这样,其他的线等优先级线程相对于它们的对等体自动放弃。如果不这样,其他的线程就不会运行。程就不会运行。1 1线程优先级线程优先级第4页/共24页2 2同步性同步性 由由于于多多线线程程在在程程序序中中引引入入了了一一个个异异步步行行为为,故故在在需需要要的的时时候候必必须须有有加加强强同同步步性性的的方方法法。举举例例来来说说,如如果果你你希希望望两两个个线线程程相相互互通通信信并并共共享享一一个个复复杂杂的的数数据据结结构构,例例如如链链表表序序列列,就就需需要要某某些些方方法法来来确确保保它它们们没没有有相相互互冲冲突突。也也就就是是说说,你你必必须须防防止止一一个个线线程程写写入入数数据据而而另另一一个个线线程程正正在在读读取取链链表表中中的的数数据据。为为此此,JavaJava在在进进程程 间间 同同 步步 性性 的的 老老 模模 式式 基基 础础 上上 实实 行行 了了 另另 外外 的的 一一 种种 方方 法法:管管 程程(monitormonitor)。管管程程是是一一种种由由C.A.R.HoareC.A.R.Hoare首首先先定定义义的的控控制制机机制制。你你可可以以把把管管程程想想象象成成一一个个仅仅控控制制一一个个线线程程的的小小盒盒子子。一一旦旦线线程程进进入入管管程程,所所有有线线程程必必须须等等待待直直到到该该线线程程退退出出了了管管程程。用用这这种种方方法法,管程可以用来防止共享的资源被多个线程操纵。管程可以用来防止共享的资源被多个线程操纵。很很多多多多线线程程系系统统将将管管程程作作为为程程序序必必须须明明确确的的引引用用和和操操作作的的对对象象。但但JavaJava提提供供一一个个清清晰晰的的解解决决方方案案,不不提提供供“Monitor”Monitor”类类;相相反反,每每个个对对象象都都拥拥有有自自己己的的隐隐式式管管程程,当当对对象象的的同同步步方方法法被被调调用用时时管管程程自自动动载载入入。一一旦旦一一个个线线程程包包含含在在一一个个同同步步方方法法中中,没没有有其其他他线线程程可可以以调调用用相相同同对对象象的的同同步步方方法法。这这就就使使你你可可以以编编写写非非常常清清晰晰和和简洁的多线程代码,因为同步支持是语言内置的。简洁的多线程代码,因为同步支持是语言内置的。第5页/共24页3 3消息传递消息传递 当当把把程程序序分分成成若若干干线线程程后后,就就要要定定义义各各线线程程之之间间的的联联系系。用用大大多多数数其其他他语语言言规规划划时时必必须须依依赖赖于于操操作作系系统统来来确确立立线线程程间间通通信信,这这样样当当然然要要增增加加花花费费。然然而而,JavaJava提提供供了了多多线线程程间间谈谈话话清清洁洁的的、低低成成本本的的途途径径通通过过调调用用所所有有对对象象都都有有的的预预先先确确定定的的方方法法。JavaJava的的消消息息传传递递系系统统允允许许一一个个线线程程进进入入一一个个对对象象的的一一个个同同步步方方法,然后在那里等待,一直等到其他线程明确通知它出来。法,然后在那里等待,一直等到其他线程明确通知它出来。Return Java的多线程系统建立于Thread类、方法以及共伴接口Runnable基础上。Thread类封装了线程的执行。既然不能直接引用运行着的线程的状态,就要通过它的代理处理它。于是Thread 实例产生了。为创建一个新的线程,程序中必须扩展Thread 或实现Runnable接口。Thread类定义了好几种方法来帮助管理线程,见教材P192页表8-1中所列。4 4ThreadThread类和类和RunnableRunnable接口接口第6页/共24页8.2 8.2 线程的创建线程的创建 本节介绍在本节介绍在JavaJava中如何创建线程。主要内容包中如何创建线程。主要内容包括主线程、多线程的创建、相关方法的使用等。括主线程、多线程的创建、相关方法的使用等。8.2.1 8.2.1 关于主线程关于主线程8.2.2 8.2.2 创建一个线程创建一个线程8.2.3 8.2.3 创建多线程创建多线程8.2.4 8.2.4 使用使用isAlive()isAlive()和和join()join()Return第7页/共24页8.2.1 8.2.1 关于主线程关于主线程 当当JavaJava程程序序启启动动时时,一一个个线线程程立立刻刻运运行行,该该线线程程通通常常就就叫叫做做程程序序的的主主线线程程(main main threadthread),因因为为它它是是程程序序开开始始时时就就执执行行的的。主主线线程程的的重重要要性性主主要要体现在两方面:体现在两方面:l l它是产生其他子线程的线程;它是产生其他子线程的线程;l l通常它必须最后完成执行,因为它执行各种关闭动作。通常它必须最后完成执行,因为它执行各种关闭动作。尽尽管管主主线线程程在在程程序序启启动动时时自自动动创创建建,但但它它可可以以由由一一个个ThreadThread对对象象控控制制。为为此此,必必须须调调用用方方法法currentThread()currentThread()获获得得它它的的一一个个引引用用,currentThreadcurrentThread()()是是ThreadThread类的公有的静态成员。它的一般形式如下类的公有的静态成员。它的一般形式如下staticThreadcurrentThread()staticThreadcurrentThread()该该方方法法返返回回一一个个调调用用它它的的线线程程的的引引用用。一一旦旦获获得得主主线线程程的的引引用用,就就可可以以像控制其他线程那样控制主线程。像控制其他线程那样控制主线程。下面我们考察一下教材下面我们考察一下教材P192P192 193193页的程序代码。页的程序代码。在上面的程序中,当前线程(当然是主线程)的引用通过调用在上面的程序中,当前线程(当然是主线程)的引用通过调用currentThreadcurrentThread()()获得,该引用保存在局部变量获得,该引用保存在局部变量t t中。然后,程序显示了线程的信息。接着,中。然后,程序显示了线程的信息。接着,程序调用程序调用setName()setName()改变线程的内部名称,线程信息又被显示。然后,一个改变线程的内部名称,线程信息又被显示。然后,一个循环数从循环数从5 5开始递减,每数一次暂停一秒。暂停是由开始递减,每数一次暂停一秒。暂停是由sleep()sleep()方法来完成的,方法来完成的,sleepsleep()()语句明确规定延迟时间是语句明确规定延迟时间是1 1毫秒。请读者注意循环外的毫秒。请读者注意循环外的try/catchtry/catch块。块。ThreadThread类的类的sleep()sleep()方法可能引发一个方法可能引发一个InterruptedExceptionInterruptedException异常,这种异常,这种情形会在其他线程想要打搅沉睡线程时发生。本例只是打印了它是否被打断情形会在其他线程想要打搅沉睡线程时发生。本例只是打印了它是否被打断的消息。在实际的程序中,必须灵活处理此类问题。的消息。在实际的程序中,必须灵活处理此类问题。Return第8页/共24页8.2.2 8.2.2 创建一个线程创建一个线程大大多多数数情情况况,通通过过实实例例化化一一个个ThreadThread对对象象来来创创建建一一个个线线程程。JavaJava定定义义了了两两种种方式:方式:l l实现实现Runnable Runnable 接口;接口;l l以继承以继承ThreadThread类的方式。类的方式。创建线程最简单的方法就是创建一个实现Runnable 接口的类,Runnable抽象了一个执行代码单元。可以通过实现Runnable接口的方法创建每一个对象的线程。为实现 Runnable 接口,一个类仅需实现一个run()的简单方法,该方法声明如下:publicvoidrun()在run()中,可以定义代码来构建新的线程。重要的是:run()方法能够像主线程那样调用其他方法,引用其他类,声明变量。仅有的不同是:run()在程序中确立另一个并发的线程执行入口。当run()返回时,该线程结束。在已经创建了实现Runnable接口的类以后,需要在类内部实例化一个Thread类的对象。Thread 类定义了好几种构造函数。我们会用到的如下:Thread(RunnablethreadOb,StringthreadName)在该构造函数中,threadOb是一个实现Runnable接口类的实例。这定义了线程执行的起点,新线程的名称由threadName定义。建立新的线程后,它并不运行直到调用其start()方法,该方法在Thread 类中定义。从本质上讲,start()执行的是一个对run()的调用。start()方法声明如下:voidstart()下面我们分别对这两种方法进行介绍:1 1实现实现RunnableRunnable接口接口第9页/共24页2 2扩展扩展ThreadThread 创创建建线线程程的的另另一一个个途途径径是是创创建建一一个个新新类类来来扩扩展展ThreadThread类类,然然后后再再创创建建该该类类的的实实例例。当当一一个个类类继继承承ThreadThread时时,它它必必须须重重载载run()run()方方法法,这这个个run()run()方方法法是是新新线线程程的的入入口口。同时,它也必须调用同时,它也必须调用start()start()方法去启动新线程执行。方法去启动新线程执行。Return 到这里,读者可能会奇怪为什么Java有两种创建子线程的方法,哪一种更好呢。所有的问题都归于一点。Thread类定义了多种方法可以被派生类重载。对于所有的方法,唯一的必须被重载的是run()方法。这当然是实现Runnable接口所需的同样的方法。很多Java程序员认为类仅在它们被加强或修改时被扩展。因此,如果你不重载Thread的其他方法,最好只实现Runnable 接口,这当然由自己决定。在本章的其他部分,我们应用实现Runnable接口的类来创建线程。3 3选择合适的方法选择合适的方法第10页/共24页8.2.3 8.2.3 创建多线程创建多线程 到目前为止,我们仅用到两个线到目前为止,我们仅用到两个线程:主线程和一个子线程。然而,我程:主线程和一个子线程。然而,我们完全可以创建所需的更多线程。例们完全可以创建所需的更多线程。例如,教材如,教材P197P197 198198页的程序创建了页的程序创建了3 3个子线程。详细分析该程序。个子线程。详细分析该程序。Return第11页/共24页8.2.4 8.2.4 使用使用isAlive()isAlive()和和join()join()如如前前所所述述,我我们们一一般般是是希希望望主主线线程程最最后后结结束束。在在上上面面的的例例子子中中,这这点点是是通通过过在在main()main()中中调调用用sleep()sleep()来来实实现现的的,经经过过足足够够长长时时间间的的延延迟迟以以确确保保所所有有子子线线程程都都先先于于主主线线程程结结束束。然然而而,这这并并不不是是一一个个好好的的解解决决方方法法。因因为为有有时时候候存存在在这这个个问问题题:一一个个线线程程如如何何知知道道另另一一线线程已经结束?幸运的是,程已经结束?幸运的是,ThreadThread类提供了解决此问题的有效方法。类提供了解决此问题的有效方法。有有两两种种方方法法可可以以判判定定一一个个线线程程是是否否结结束束:第第一一,可可以以在在线线程程中中调调用用isAliveisAlive()()。这种方法由。这种方法由ThreadThread定义,它的一般形式如下定义,它的一般形式如下finalbooleanisAlive()finalbooleanisAlive()如如果果所所调调用用线线程程仍仍在在运运行行,isAlive()isAlive()方方法法返返回回truetrue,如如果果不不是是则则返回返回falsefalse。但但isAlive()isAlive()很很少少用用到到,等等待待线线程程结结束束的的更更常常用用的的方方法法是是调调用用joinjoin()(),描述如下,描述如下finalvoidjoin()throwsInterruptedExceptionfinalvoidjoin()throwsInterruptedException 该方法等待所调用线程结束,该名字来自于要求线程等待直到指该方法等待所调用线程结束,该名字来自于要求线程等待直到指定线程参与的概念。定线程参与的概念。join()join()的附加形式允许给等待指定线程结束定的附加形式允许给等待指定线程结束定义一个最大时间。详细分析教材义一个最大时间。详细分析教材P199P199 200200页的程序。页的程序。Return第12页/共24页8.3 8.3 线程的优先级线程的优先级 线线程程优优先先级级被被线线程程调调度度用用来来判判定定何何时时某某个个线线程程允允许许运运行行。理理论论上上,优优先先级级高高的的线线程程比比优优先先级级低低的的线线程程获获得得更更多多的的CPUCPU时时间间。实实际际上上,线线程程获获得得的的CPUCPU时时间间通通常常由由包包括括优优先先级级在在内内的的多多个个因因素素决决定定。一一个个优优先先级级高高的的线线程程自自然然比优先级低的线程优先。比优先级低的线程优先。理理论论上上,等等优优先先级级线线程程有有同同等等的的权权利利使使用用CPUCPU,但但你你必必须须小小心心。需需要要记记住住的的是是,JavaJava是是被被设设计计成成能能在在很很多多环环境境下下工工作作的的。不不同同环环境境下下实实现现多多任任务务处处理理从从本本质质上上来来看看是是可可能能的的。为为安安全全起起见见,等等优优先先级级线线程程有有时时候候也也受受到到控控制制。这这保保证证了了所所有有线线程程在在无无优优先先级级的的操操作作系系统统下下都都有有机机会会运运行行。实实际际上上,在在无无优优先先级级的的环环境境下下,多多数数线线程程仍仍然然有有机机会会运运行行,因因为为很很多多线线程程不不可可避避免免地地会会遭遭遇遇阻阻塞塞,例例如如等等待待输输入入输输出出。遇遇到到这这种种情情形形,阻阻塞塞的的线线程程挂挂起起,其其他他线线程程运运行行。但但是是如如果果你你希希望望多多线线程程执执行行得得顺顺利利的的话话,最最好好不不要要采采用用这这种种方方法法。同同样样,有有些些类类型型的的任任务务是是占占CPUCPU的的。对对于于这这些些支支配配CPUCPU类类型型的的线线程程,有有时时你你希希望能够支配它们,以便使其他线程可以运行。望能够支配它们,以便使其他线程可以运行。设设置置线线程程的的优优先先级级,用用setPriority()setPriority()方方法法,该该方方法法也也是是ThreadThread的的成成员。它的通常形式为员。它的通常形式为 finalvoidsetPriority(intlevel)finalvoidsetPriority(intlevel)这里,这里,levellevel指定了对所调用的线程的新的优先权的设置。指定了对所调用的线程的新的优先权的设置。LevelLevel的值必须的值必须在在MIN_PRIORITYMIN_PRIORITY到到MAX_PRIORITYMAX_PRIORITY范围内。通常,它们的值分别是范围内。通常,它们的值分别是1 1和和1010。要返回一个线程为默认的优先级,指定要返回一个线程为默认的优先级,指定NORM_PRIORITYNORM_PRIORITY,通常值为,通常值为5 5。这些。这些优先级在优先级在ThreadThread中都被定义为中都被定义为finalfinal型变量。型变量。Return第13页/共24页8.4 8.4 线程同步线程同步 当两个或两个以上的线程需要共享资源,它们当两个或两个以上的线程需要共享资源,它们需要某种方法来确定资源在某一刻仅被一个线程占需要某种方法来确定资源在某一刻仅被一个线程占用,达到此目的的过程叫做同步用,达到此目的的过程叫做同步(synchronizationsynchronization)。)。JavaJava为此提供了独特的、为此提供了独特的、很有效的支持机制。很有效的支持机制。8.4.1 8.4.1 使用同步方法使用同步方法8.4.2 8.4.2 同步语句同步语句Return第14页/共24页8.4.1 8.4.1 使用同步方法使用同步方法 同步的关键是管程(也叫信号量,即同步的关键是管程(也叫信号量,即semaphoresemaphore)的概念。管程是一个互斥独占锁定)的概念。管程是一个互斥独占锁定的对象,或称互斥体(的对象,或称互斥体(mutexmutex)。在给定的时间,)。在给定的时间,仅有一个线程可以获得管程。当一个线程需要锁仅有一个线程可以获得管程。当一个线程需要锁定时,它必须进入管程。所有其他的试图进入已定时,它必须进入管程。所有其他的试图进入已经锁定的管程的线程必须挂起直到第一个线程退经锁定的管程的线程必须挂起直到第一个线程退出管程。这些其他的线程被称为等待管程。出管程。这些其他的线程被称为等待管程。我我们们可可以以用用两两种种方方法法同同步步化化代代码码。通通过过调调用用sleepsleep()(),call()call()方方法法允允许许执执行行转转换换到到另另一一个个线线程程。两两者者都都 包包 括括 synchronizedsynchronized关关 键键 字字 的的 运运 用用。分分 析析 教教 材材P204P204页的示例。页的示例。Return第15页/共24页8.4.2 8.4.2 同步语句同步语句 尽尽管管在在创创建建的的类类的的内内部部创创建建同同步步方方法法是是获获得得同同步步的的简简单单和和有有效效的的方方法法,但但它它并并非非在在任任何何时时候候都都有有效效。假假设设你你想想获获得得不不为为多多线线 程程 访访 问问 设设 计计 的的 类类 对对 象象 的的 同同 步步 访访 问问,也也 就就 是是 该该 类类 没没 有有 用用 到到synchronizedsynchronized方方法法。而而且且,该该类类不不是是你你自自己己,而而是是第第三三方方创创建建的的,就就 不不 能能 获获 得得 它它 的的 源源 代代 码码。这这 样样,就就 不不 能能 在在 相相 关关 方方 法法 前前 加加synchronizedsynchronized修修饰饰符符。怎怎样样才才能能使使该该类类的的一一个个对对象象同同步步化化呢呢?解解决决的的方方法法很很简简单单:只只需需将将对对这这个个类类定定义义的的方方法法的的调调用用放放入入一一个个synchronizedsynchronized块内就可以了。块内就可以了。下面是下面是synchronizedsynchronized语句的一般形式语句的一般形式synchronized(object)synchronized(object)/statementstobesynchronized/statementstobesynchronized 其中,其中,objectobject是对同步对象的引用。如果你想要同步的只是对同步对象的引用。如果你想要同步的只是一个语句,那么不需要花括号。一个同步块确保对是一个语句,那么不需要花括号。一个同步块确保对objectobject成成员方法的调用仅在当前线程成功进入员方法的调用仅在当前线程成功进入objectobject管程后发生。管程后发生。Return第16页/共24页8.5 8.5 线程间通信线程间通信 前面的例子无条件地阻塞了其他线程异步访前面的例子无条件地阻塞了其他线程异步访问某个方法。问某个方法。JavaJava对象中隐式管程的应用是很强对象中隐式管程的应用是很强大的,但是我们可以通过进程间通信达到更微妙大的,但是我们可以通过进程间通信达到更微妙的境界,这在的境界,这在JavaJava中是很简单的。中是很简单的。8.5.1 Java8.5.1 Java中的线程通讯中的线程通讯8.5.2 8.5.2 关于死锁关于死锁Return第17页/共24页8.5.1 Java8.5.1 Java中的线程通讯中的线程通讯 多多线线程程通通过过把把任任务务分分成成离离散散的的和和合合乎乎逻逻辑辑的的单单元元代代替替了了事事件件循循环环程程序序。线线程程还还有有另另外外一一个个优优点点:它它远远离离了了轮轮询询。轮轮询询通通常常由由重重复复监监测测条条件件的的循循环环实实现现。一旦条件成立,就要采取适当的行动。这浪费了一旦条件成立,就要采取适当的行动。这浪费了CPUCPU时间。时间。为为避避免免轮轮询询,JavaJava包包含含了了通通过过wait()wait(),notify()notify()和和notifyAll()notifyAll()方方法法实实现现的的一一个个进进程程间间通通信信机机制制。这这些些方方法法在在对对象象中中是是用用finalfinal方方法法实实现现的的,所所以以所所有有的的类类都都含含有有它它们们。这这三三个个方方法法仅仅在在synchronizedsynchronized方方法法中中才才能能被被调调用用。尽尽管管这这些些方方法法从从计计算算机机科科学学远远景景方方向向上上来来说说具具有有概概念念的的高高度度先先进进性性,实实际际中中用起来却是很简单的。用起来却是很简单的。wait()wait()wait()wait()告告知知被被调调用用的的线线程程放放弃弃管管程程进进入入睡睡眠眠直直到到其其他他线线程程进进入入相相同同管程并且调用管程并且调用notify()notify()。notify()notify()notify()notify()恢复相同对象中第一个调用恢复相同对象中第一个调用wait()wait()的线程。的线程。notifyAll()notifyAll()notifyAll()notifyAll()恢复相同对象中所有调用恢复相同对象中所有调用wait()wait()的线程。的线程。这些方法在这些方法在ObjectObject中被声明,如下所示中被声明,如下所示finalvoidwait()throwsInterruptedExceptionfinalvoidwait()throwsInterruptedExceptionfinalvoidnotify()finalvoidnotify()finalvoidnotifyAll()finalvoidnotifyAll()wait()wait()存存在在的的另另外外的的形形式式允允许许你你定定义义等等待待时时间间。分分析析教教材材P207P207 210210页页的的程程序段。序段。Return第18页/共24页8.5.2 8.5.2 关于死锁关于死锁 需需 要要 避避 免免 的的 与与 多多 任任 务务 处处 理理 有有 关关 的的 特特 殊殊 错错 误误 类类 型型 是是 死死 锁锁(deadlockdeadlock)。死死锁锁发发生生在在当当两两个个线线程程对对一一对对同同步步对对象象有有循循环环依依赖赖关关系系时时。例例如如,假假定定一一个个线线程程进进入入了了对对象象X X的的管管程程而而另另一一个个线线程程进进入入了了对对象象Y Y的的管管程程。如如果果X X的的线线程程试试图图调调用用Y Y的的同同步步方方法法,它它将将像像预预料料的的一一样样被被锁锁定定。而而Y Y的的线线程程同同样样希希望望调调用用X X的的一一些些同同步步方方法法,线线程程永永远远等等待待,因因为为为为到到达达X X,必必须须释释放放自自己己的的Y Y的的锁锁定定以以使使第第一一个个线线程程可可以以完完成成。死死锁锁是是很很难难调调试试的的错错误误,这这是是因因为为:第第一一,通通常常它它极极少少发发生生,只只有有到到两两线线程程的的时时间间段段刚刚好好符符合合时时才才能能发发生生;第第二二,它它可可能能包包含含多多于于两两个个的的线线程程和和同同步步对对象象。也也就就是是说说,死死锁锁在在比比刚刚讲讲述述的的例例子有更多复杂的事件序列的时候可以发生。子有更多复杂的事件序列的时候可以发生。为充分理解死锁,观察它的行为是很有用的。教材为充分理解死锁,观察它的行为是很有用的。教材P211P211 212212页的例子生成了两个类,页的例子生成了两个类,A A和和B B,分别有,分别有foo()foo()和和bar()bar()方法。这两方法。这两种方法在调用其他类的方法前有一个短暂的停顿。主类,名为种方法在调用其他类的方法前有一个短暂的停顿。主类,名为DeadlockDeadlock,创建了,创建了A A和和B B的实例,然后启动第二个线程去设置死锁的实例,然后启动第二个线程去设置死锁环境。环境。foo()foo()和和bar()bar()方法使用方法使用sleep()sleep()强迫死锁现象发生。强迫死锁现象发生。程序死锁,需要按程序死锁,需要按CTRL-CCTRL-C来结束程序。在来结束程序。在PCPC机上按机上按CTRL-CTRL-BREAKBREAK(或在(或在SolarisSolaris下按下按CTRL-CTRL-)可以看到全线程和管程缓冲堆。)可以看到全线程和管程缓冲堆。Return第19页/共24页8.6 8.6 线程的控制线程的控制 本节讨论有关线程控制的问题,包括线程的挂本节讨论有关线程控制的问题,包括线程的挂起、恢复、终止等方面的问题。起、恢复、终止等方面的问题。8.6.1 8.6.1 挂起、恢复和终止线程挂起、恢复和终止线程8.6.2 Java 28.6.2 Java 2中的线程控制中的线程控制8.6.3 8.6.3 使用使用instanceofinstanceofReturn第20页/共24页8.6.1 8.6.1 挂起、恢复和终止线程挂起、恢复和终止线程 有有时时,线线程程的的挂挂起起是是很很有有用用的的。例例如如,一一个个独独立立的的线线程程可可以以用用来来显显示示当当日日的的时时间间。如如果果用用户户不不希希望望用用时时钟钟,线线程程被被挂挂起起。在在任任何何情情形形下下,挂起线程是很简单的,一旦挂起,重新启动线程也是一件简单的事。挂起线程是很简单的,一旦挂起,重新启动线程也是一件简单的事。挂挂起起、终终止止和和恢恢复复线线程程机机制制在在Java Java 2 2和和早早期期版版本本中中有有所所不不同同。尽尽管管你你运运用用Java Java 2 2的的途途径径编编写写代代码码,仍仍需需了了解解这这些些操操作作在在早早期期JavaJava环环境境下下是是如如何何完完成成的的。例例如如,也也许许你你需需要要更更新新或或维维护护老老的的代代码码,就就需需要要了了解解为为什什么么Java Java 2 2会会有有这这样样的的变变化化。因因为为这这些些原原因因,下下面面内内容容说说明明了了执行线程控制的原始方法,接着是执行线程控制的原始方法,接着是Java 2Java 2的方法。的方法。先先 于于 Java2Java2的的 版版 本本(或或 更更 早早 版版 本本