版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Java语言程序设计第8章多线程本章位置掌握多线程的创建1掌握线程的优先级2能力点第8章多线程Java语言程序设计主要内容1多线程的基本概念2多线程的实现机制3线程的状态和线程的控制4线程的同步5案例分析Java语言程序设计第8章多线程6任务训练
序列化程序的特点是只有一个入口、一个可执行的命令序列、一个出口。在程序执行的任何时刻,只有一个执行点。线程和序列化程序类似,也有一个入口、执行序列和出口,执行时也只有一个执行点。但是线程不是程序,它不能自己独立运行,只有在程序中执行。线程是一个程序内部的顺序控制流,即程序中的一条执行路径。当同一个程序中有多条执行路径并发执行时,就称之为多线程(Multi-Thread)。换句话说,在多线程中允许一个程序创建多个并发执行的线程来完成各自的任务。
多线程的应用范围很广。在一般情况下,程序的一些部分同特定的事件或资源联系在一起,同时又不想为它而暂停程序其他部分的执行,这种情况下,就可以考虑创建一个线程,令它与那个事件或资源关联到一起,并让它独立于主程序运行。通过使用线程,可以避免用户在运行程序和得到结果之间的停顿,还可以让一些任务(如打印任务)在后台运行,而用户则在前台继续执行一些其他的工作。总之,利用多线程技术,可以使编程人员方便地开发出能同时处理多个任务的功能强大的应用程序。8.1多线程的基本概念Java语言程序设计第8章多线程8.2多线程的实现机制创建多线程有两种方法:继承Thread类和实现Runnable接口。在下面的小节里,将分别详细讲解。
Java语言程序设计第8章多线程
一个Thread类的一个实例对象就是Java程序的一个线程,所以Thread类的子类的实例对象也是Java程序的一个线程。因此构造Java程序可以通过构造类Thread的子类的实例对象来实现。构造类Thread的子类主要目的是为了让线程类的实例对象能够完成线程程序所需要的功能。
通过这种方法构造出来的线程在程序执行时的代码被封装在类Thread或其子类的成员方法run中。为了使新构造出来的线程能完成所需要的功能。新构造出来的线程类应覆盖Thread类的成员方法run()。
线程的启动或运行并不是调用成员方法run()。而是调用成员方法start()达到间接调run()方法,线程的运行实际上就是执行线程的成员方法run()。
直接方式创建线程的步骤如下:(1)定义一个线程(Thread)子类;(2)在该线程子类中定义run()方法;(3)在run()方法中定义此线程的具体操作;(4)在其他类的方法中创建此线程的实例对象,并用start()方法启动线程。8.2.1继承Thread类8.2多线程的实现机制第8章多线程Java语言程序设计【例10-1】通过继承Thread类创建线程,在主控程序中同时运行两个线程。输出奇数和偶数。
结果如下:Java语言程序设计第8章多线程8.2多线程的实现机制由于Java不支持多继承性,如果用户需要类以线程方式运行且继承其他所需要的类,就必须实现Runnable接口。Runnable接口包含了与Thread类一致的基本方法。事实上,Runnable接口只有一个run()方法,所以实现这个接口的程序必须要定义run()方法的具体内容,用户新建线程的操作也由这个方法来决定。定义好接口类后,程序中如果需要使用线程时,只要以这个实现了run()方法的类为参数创建系统类Thread的对象,就可以把实现的run()方法继承过来。间接方式创建线程的步骤如下:(1)定义一个Runnable接口类;(2)在此接口中定义一个run()方法;(3)在run()方法中定义线程的操作;(4)在其他类的方法中创建此Runnable接口类的实例对象,并以此实例对象作为参数创建线程类对象。(5)用start()方法启动线程。8.2.2实现Runnable接口第8章多线程Java语言程序设计8.2多线程的实现机制【例10-2】使用Runnable接口方法创建线程和启动线程。
结果如下:Java语言程序设计第8章多线程8.2多线程的实现机制8.3线程的状态和线程的控制Java语言程序设计第8章多线程一个线程从创建、启动到终止的整个过程就叫做一个生命周期,在其间的任何时刻,线程总是处于某个特定的状态。这些状态有如下的五个,它们之间的转换如图10-1所示。
图10-1线程基本状态转换图8.3.1线程的状态和生命周期8.3线程的状态和线程的控制Java语言程序设计第8章多线程1、新建状态也称之为新线程状态,即创建了一个线程类的对象后,产生的新线程进入创建状态。实现语句如下:ThreadmyThread=newmyThreadClass();这是一个空的线程对象,run()方法还没有执行,若要执行它,系统还需对这个线程进行登记并为它分配系统资源,这些工作由start()方法来完成。2、就绪状态
该状态也叫可执行状态,当一个被创建的线程调用start()方法后便进入可执行状态。对应的程序语句为:myThread.start();//产生所需系统资源,安排运行,并调用run()方法此时该线程处于准备占用处理机运行的状态。即它们已经被放到就绪队列中等待执行。至于该线程何时才被真正执行,则取决于线程的优先级和就绪队列的当前状况。只有操作系统调度到该线程时,才真正占用了处理机并运行run()方法。所以这种状态并不是执行中的状态。3、执行状态当处于可执行状态的线程被调度并获得了CPU等执行必需的资源时,便进入到该状态,即运行了run()方法。4、阻塞状态又叫不可执行状态,当下面的四种情况之一发生时,线程就会进入阻塞状态:调用了sleep()方法。调用了wait()方法,为的是等待一个条件变量。调用了suspend()方法。输入输出流中发生线程阻塞。例如:ThreadmyThread=newmyThreadClass();myThread.start();try{myThread.sleep(20000);}catch(InterruptedExceptione){}
上面的例子当中调用了sleep()方法使myThread线程休眠了20秒,20秒后又可恢复运行。如果一个线程处于阻塞状态,那么这个线程暂时无法进入就绪队列。处于阻塞状态的线程通常需要由某些事件才能唤醒,至于由什么事件唤醒该线程,则取决于挂起的原因。针对上面四种情况,都有特定的唤醒方法与之对应,对应方法如下:若调用了sleep()方法后,线程处于睡眠状态,该方法的参数为睡眠时间,当这个时间过去后,线程进入可执行状态。若线程在等待一个条件变量,那么要想停止等待的话,需要该条件变量所在的对象调用notify或notifyAll()方法。若线程调用了suspend()方法,须有其他线程调用resume()方法来恢复该线程的执行。若I/O流中发生线程阻塞,则规定的I/O指令将结束这种不可执行状态。5、死亡状态
死亡状态又称作终止状态或停止状态。处于这种状态的线程已经不能够再继续执行。其中的原因可能是线程已经执行完毕,正常的撤销;也可能是被强制终止,例如通过执行stop()或destroy()方法来终止线程。8.3.1线程的状态和生命周期Java语言程序设计第8章多线程8.3线程的状态和线程的控制1、终止线程
当一个线程终止后,其生命周期就结束了,便进入死亡状态。终止线程的执行可以用stop()方法,需要注意的是此时并没有消灭这个线程,只是停止了线程的执行,并且这个线程不能用start()方法重新启动。一般情况下不用stop()方法终止一个线程,只是简单的让它执行完而已。很多复杂的线程程序将需要控制每一个线程,在这种情况下会用到stop()方法。2、测试线程状态
一个已经停止运行的线程是不能用start()方法重新启动的。因此,为了避免出错可以测试一个线程是否处于被激活的状态,方法为isAlive()。一个线程已经启动而且没有停止就被认为是激活的。如果线程t是激活的,t.isAlive()将返回true,但该线程是可运行的或是不可运行的,不能做进一步的解释分析;如果返回false,则该线程是新创建或已被终止的。8.3.2线程的控制Java语言程序设计第8章多线程3、线程的暂停和恢复(1)sleep()通过调用该方法可以指定线程睡眠一段时间。当线程睡眠到指定的时间后,不会立即进入执行状态,而只是参与调度执行。这是因为当前线程正在运行时,不会立刻放弃处理机,除非这时有更高优先级的线程参与调度或者是当前线程由于某种原因被阻塞,在时间片方式下也可以是当前的时间片用完。(2)suspend()和resume()调用suspend()方法使线程暂停,并不是永久的停止线程,这可以用resume()方法重新激活线程。(3)join()join()方法将使当前线程进入等待状态,直至join()方法所调用的线程结束。例如已经生成并运行了一个线程tt,而在另一个线程中执行timeout()方法,其定义如下:publicvoidtimeout(){//暂停该线程,等待其他线程(tt)结束tt.join();//其他线程结束后,继续执行该线程……}
这样,在执行timeout()方法以后,现在的线程将被阻塞,直到tt运行结束。
也可以使用”join(longtimeout)”限定等待时间,单位为毫秒。8.3线程的状态和线程的控制8.4线程的同步Java语言程序设计第8章多线程
在多线程的程序中,要求各个线程对共享资源的访问是互斥的。比如,铁路售票系统,有4个售票点发售某日某次列车的100张车票,票(ticket)是共享资源,其中,一个售票点在发售某张票的时候哦,其余售票点便不能进行售票,必须等这个售票点发售完这张票,并释放对共享资源(ticket)的使用权后才能进行售票。如下面的代码:if(ticket>0)System.out.println(Thread.currentThread().getName()+”issailingticket”+tickets--);即当一个售票线程运行到if(ticket>0)语句后,CPU必须等到if语句执行完毕才去执行其他售票线程的相应代码段。Java中对共享数据操作的并发控制是采用传统的封锁技术。在Java中为保证线程对共享资源操作的完整性,用synchronized关键字为共享资源加锁来解决,称为互斥锁。每个共享资源对象都有一个互斥锁标记,保证任一时刻只能有一个线程访问该对象。8.4.1共享受限资源8.4线程的同步Java语言程序设计第8章多线程synchronized关键字的语法格式有两种:synchronized(object){同步代码段}//object可以是任意一个对象将前面的售票代码修改一下,使之具有同步的效果:Stringstr=newString(““);synchronized(str){if(ticket>0)System.out.println(Thread.currentThread().getName()+”issailingticket”+tickets--);}程序中用Stringstr=newString(““)语句随便产生了一个对象,用于后面的同步代码段。(2)synchronized作为方法的修饰字,使该方法成为同步方法。当一个线程在使用实例对象的某个同步方法时,试图调用该实例对象任何同步方法的其他线程都必须等待,直至该线程退出同步方法。然后该实例对象的不同步方法仍然可以被调用。例如定义pop()为同步方法:publicsynchronizedvoidpop(){….}上一节介绍了线程之间在访问对象的临界区时,需要使用同步以实现线程的互斥。有时,多个线程之间往往需要共同协作。比如,线程A往缓冲区中写数据,线程B从缓冲区中取数据,当缓冲区中没有数据时,线程B必须等待,当缓冲区满时,线程A必须等待。
Java通过wait()方法、notify()方法和notifyAll()方法实现线程间的协作。这些方法在对象中是用final方法实现的,所以所有的类都包含它们。这三个方法仅在synchronized方法中才能被调用。wait():告知被调用的线程进入睡眠,直到其他线程进入并且调用notify()方法。notify():恢复相同对象中第一个调用wait()方法的线程。notifyAll():恢复相同对象中所有调用wait()方法的线程。具有最高优先级的线程将最先运行。这些方法在Object中被声明,如下所示:finalvoidwait()throwsInterrupedExceptionfinalvoidnotify()finalvoidnotifyAll()下面的例子程序错误地实现了一个简单生产者/消费者的应用问题。它由四个类组成:Q类设法获得同步的序列;Producer类产生排队的线程对象;Consumer类消费序列的线程对象;Exam10_3类创建单个Q、Producer和Consumer类的小类。8.4.2线程间的协作Java语言程序设计第8章多线程8.4线程的同步【例10-3】生产者/消费者问题。Java语言程序设计第8章多线程结果:尽管Q类中的put()方法和get()方法是同步的,但是没有东西阻止生产者超越消费者,也没有东西阻止消费者消费同样的序列两次。这样,就得到下面的错误输出(输出将随处理器速度和装载的任务而改变):8.4线程的同步【例10-3】同步后的生产者/消费者问题。Java语言程序设计第8章多线程内部的get()方法、wait()方法被调用。这时执行挂起操作,直到Producer告知数据已经预备好。此时内部的get()方法被恢复执行。获得数据后,get()方法调用notify()方法,并告诉Producer可以向序列中输入更多数据。在put()方法内,wait()方法挂起执行,直到Consumer取走了序列中的项目。当继续执行时,下一个数据项目会被放入序列,notify()方法被调用,从而通知Consumer应该移走相应数据。下面是该序列的输出,它清楚地显示了同步行为。8.4线程的同步在多线程竞争使用多资源的程序中,有可能出现死锁的情况。这种情况发生在一个线程等待另一个线程所持有的锁,而那个线程又在等待第一个线程持有的锁的时候。每个线程都不能继续运行,除非另一线程运行完同步程序块。而恰恰因为任何一个线程都不能继续运行,所以这些线程都无法运行完同步程序块。图10-2示意了上面问题的产生机制,其程序代码如下:第8章多线程Java语言程序设计图10-2死锁程序结构示意图8.4线程的同步【例10-5】死锁。程序运行结果如下:Java语言程序设计第8章多线程8.4线程的同步Java虚拟机允许一个应用程序可以拥有多个同时执行的线程,而众多的线程中,哪一个线程先执行,哪一个线程后执行,取决于线程的优先级(Priority)。线程的优先级由整数值1~10来表示,优先级越高,越先执行;优先级越低,越晚执行;优先级相同时,则遵循队列的”先进先出”的原则。有几个与优先级相关的整数常量:MIN_PRIORITY:线程能具有的最小优先级(1)。MAX_PRIORITY:线程能具有的最大优先级(10)。NORM-PRIORITY:线程的常规优先级(5)。当线程创建时,优先级默认是由NORM-PRIORITY标识的整数。Thread类与优先级相关的方法有:setPriotity()和getPriotity()。setPriotity()方法用来设置线程的优先级,带一个整数型参数作为线程的优先级,其范围必须在MIN_PRIORITY和MAX_PRIORITY之间,并且不大于线程的Thread对象所属线程组的优先级。当一个在可执行状态队列中排队的线程被分配到了CPU等资源而进入运行状态后,这个线程就称为是被”调度”或称为被线程调度管理器选中了。Java支持一种”抢占式”(preemptive)调度方式。抢占式是和协作式(cooperative)相对的概念。所谓协作式,是指一个执行单元一旦获得某个资源的使用权,别的执行单元就无法剥夺,即使其他线程的优先级更高,而抢占式则与之相反。比如,在一个低优先级线程的执行过程中,来了一个高优先级线程,若在协作式调度系统中,这个高优先级线程必须等待低优先级线程的时间片执行完毕,而抢占式调度方式则不必,可以直接把控制权抢占过来。由于Java的线程调度遵循的是抢占式,因此,为使低优先级线程能够有机会运行,较高优先级线程可以进入”睡眠”(sleep)状态。进入睡眠状态的线程必须在被唤醒之后才能继续执行。8.4.3线程的调度和优先级第8章多线程Java语言程序设计8.4线程的同步8.5案例分析
利用本章所学的多线程相关知识完成一个具有一定功能的综合实例。Java语言程序设计第8章多线程5个人排队买电影票,售票员只有一张5元的零钱,电影票5元一张。假设5个人的名字及排队顺序是:赵、钱、孙、李、周。赵拿一张20元的人民币买2张票;钱拿1张20元的人民币买1张票;孙拿1张10元的人民币买1张票;李拿一张10元的人民币买2张票;周拿1张5元的人民币买1张票。售票员必须按如下规则找零钱:
(1)如果20元买2张票,允许找零:1张10元;不允许找零:2张5元。(2)如果20元买1张票,允许找零:1张5元,1张10元;不允许找零:3张5元。(3)如果10元买1张票,允许找零:1张5元。8.5.1案例情景——模拟排队买票8.5案例分析第8章多线程Java语言程序设计程序运行的结果可能如下:8.5.2运行结果第8章多线程Java语言程序设计8.5案例分析8.6任务训练—多线程使用Java语言程序设计第8章多线程(1)掌握多线程的含义;(2)掌握线程的创建;(3)掌握线程的生命周期;(4)掌握多线程的同步。8.6.1训练目的8.6任务训练—多线程使用第8章多线程Java语言程序设计1.对正文中各段代码编写完整的程序段代码。2.完成思考与练习中程序的编写与调试。3.模拟学生上课:答疑课上,老师逐一回答学生的问题,同一时刻教师只能回答一个学生的问题,其他学生等到前面同学的问题解决后,再向老师提出问题。
【参考程序】:packagechapter10;publicclassClassroom{ publicstaticvoidmain(String[]args){ Teacherth=newTeacher();Studentst1=newStudent(th,"张一号的问题");Studentst2=newStudent(th,"李二号的问题");Studentst3=newStudent(th,"陈三号的问题");try{st1.t.join();st2.t.join();st3.t.join();}catch(InterruptedExceptione){}}}classTeacher{publicvoidanswer(Stringmsg){System.out.print("["+msg);try{Thread.sleep((int)(Math.random()*200));}catch(InterruptedExceptione){}System.out.println("]回答完毕。");classStudentimplementsRunnable{privateStringmsg;privateTeacherth;Threadt;publicStudent(Teacherth,Strings){this.th=th;msg=s;t=newThread(this);t.start();}publicvoidrun(){synchronized(th){th.answer(msg);}8.6.2训练内容第8章多线程Java语言程序设计结果:8.6任务训练—多线程使用8.7拓展知识Java语言程序设计第8章多线程1、问:线程与进程有什么关系?8.7拓展知识第8章多线程Java语言程序设计答:线程是比进程更小的执行单位。一个进程在其执行过程中可以产生多个线程,每一个线程就是一个程序内部的一条执行线索,这些线程可以交替运行。多任务与多线程是两个不同的概念。前者是针对操作系统而言的,表示操作系统可以同时运行多个应用程序;后者是针对一个程序而言的,表示在一个程序内部可以同时执行多
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025-2026学年林芝市重点中学学业水平测试及答案含解析
- 2025-2026学年黑龙江省哈尔滨市哈尔滨风华中学第二学期期末学业质量阳光指标调研卷初三数学试题含解析
- 护理操作:肌肉注射方法详解
- 锤炼写作语言让文章更美
- 2026三年级数学上册 中间有0的退位减法
- 急救护理呼吸管理培训
- 心理咨询室安全责任制度
- 快递库房责任制度
- 意识形态责任制追究制度
- 执法办案区责任制度
- 2025年课件-(已瘦身)2023版马原马克思主义基本原理(2023年版)全套教学课件-新版
- QCT1177-2022汽车空调用冷凝器
- 外国文学史(下)-马工程
- 住院医师大课-糖尿病病人的麻醉-罗贞
- GB/T 42061-2022医疗器械质量管理体系用于法规的要求
- 丁往道英语写作手册课件
- 创新型城市建设的工作思路课件
- EHS有感领导(培训)
- 化工过程数值模拟及Aspen-Plus软件应用
- 苏教版一年级科学下册全册教案(常用)
- 2008年全国中学生生物竞赛(试卷及答案解析)
评论
0/150
提交评论