您好,欢迎访问三七文档
当前位置:首页 > 办公文档 > 招标投标 > 94第11章 Java多线程
第11章Java多线程程序迄今为止,我们开发的Java程序大多是单线程的,即一个程序只有一条从头至尾的执行线索,当程序执行过程中因为等待某个I/O操作而受阻时,其它部分的程序同样无法执行。然而现实世界中很多过程都具有多条线索同时工作,例如生物的进化,就是多方面多种因素共同作用的结果,再如服务器可能需要同时处理多个客户机的请求等,这就需要我们编写的程序也要支持多线程的工作。多线程是指同时存在几个执行体,按几条不同的执行线索共同工作的情况。Java语言的一个重要功能特点就是内置对多线程的支持,它使得编程人员可以很方便地开发出具有多线程功能、能同时处理多个任务的功能强大的应用程序。在Java语言中,不仅语言本身有多线程的支持,可以方便地生成多线程的程序,而且运行环境也利用多线程的应用程序并发提供多种服务。11.1Java中的线程11.1.1线程的基本概念程序是一段静态的代码,它是应用软件执行的蓝本。进程是程序的一次动态执行过程,它对应了从代码加载、执行到执行完毕的一个完整过程,这个过程也是进程从产生、发展到消亡的过程。作为执行蓝本的同一段程序,可以被多次加载到系统的不同内存区域分别执行,形成不同的进程。线程是比进程更小的执行单位。一个进程在其执行过程中,可以产生多个线程,形成多条执行线索。每条线索,即每个线程也有它自身的产生、存在和消亡的过程,是一个动态的概念。我们知道,每个进程都有一段专用的内存区域,并以PCB作为它存在的标志,与此不同的是,线程间可以共享相同的内存单元(包括代码与数据),并利用这些共享单位来实现数据交换、实时通信与必要的同步操作。多线程的程序能更好地表述和解决现实世界的具体问题,是计算机应用开发和程序设计的一个必然发展趋势。Java提供的多线程功能使得在一个程序里可同时执行多个小任务,CPU在线程间的切换非常迅速,使人们感觉到所有线程好像是同时进行似的。多线程带来的更大的好处是更好的交互性能和实时控制性能,当然,实时控制性能还取决于操作系统本身。11.1.2线程的状态和生命周期每个Java程序都有一个默认的主线程,对于Application,主线程是main()方法执行的线索;对于Applet,主线程指挥浏览器加载并执行Java小程序。要想实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类及其子类对象来表示线程,新建设的线程在它的一个完整的生命周期中通常要经历如下的五种状态:1.新建当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态。此时第第第11章Java多线程程序177它已经有了相应的内存空间和其它资源,并已被初始化。2.就绪处于新建状态的线程被启动后,将进入线程队列排队等待CPU时间片,此时它已经具备了运行的条件,一旦轮到它来享用CPU资源时,就可以脱离创建它的主线程独立开始自己的生命周期了。另外,原来处于阻塞状态的线程被解除阻塞后也将进入就绪状态。3.运行当就绪状态的线程被调度并获得处理器资源时,便进入运行状态。每一个Thread类及其子类的对象都有一个重要的run()方法,当线程对象被调度执行时,它将自动调用本对象的run()方法,从第一句开始顺序执行。run()方法定义了这一类线程的操作和功能。4.阻塞一个正在执行的线程如果在某些特殊情况下,如被人为挂起或需要执行费时的输入输出操作时,将让出CPU并暂时中止自己的执行,进入阻塞状态。阻塞时它不能进入排队队列,只有当引起阻塞的原因被消除时,线程才可以转入就绪状态,重新进到线程队列中排队等待CPU资源,以便从原来终止处开始继续执行。5.死亡处于死亡状态的线程不具有继续运行的能力。线程死亡的原因有两个:一个是正常运行的线程完成了它的全部工作,即执行完了run()方法的最后一个语句并退出;另一个是线程被提前强制性的终止,如通过执行stop()方法或destroy()终止线程。由于线程与进程一样是一个动态的概念,所以它也像进程一样有一个从产生到消亡的生命周期,如图11-1所示:新建就绪运行死亡阻塞图11-1线程状态的改变线程在各个状态之间的转化及线程生命周期的演进是由系统运行的状况、同时存在的其他线程和线程本身的算法所共同决定的。在创建和使用线程时应注意利用线程的方法宏观地控制这个过程。11.1.3线程调度与优先级处于就绪状态的线程首先进入就绪队列排队等候处理器资源,同一时刻在就绪队列中的线程可能有多个,它们各自任务的轻重缓急程度不同。例如用于屏幕显示的线程需要尽快地被执行,而用来收集内存碎片的垃圾回收线程则不那么紧急,可以等到处理器较空闲时再执行。为了体现上述差别,使工作得更加合理,多线程系统会给每个线程自动分配一个线程的优先级,任务较紧急重要的线程,其优先级就较高;相反则较低。在线程排队时,优先级高的线程可以排在较前的位置,能优先享用到处理器资源;而优先级较低的线程则只能等到排在它前面的高优先级线程执行完毕之后才能获得处理器资源。对于优先极相同的线程,则遵循队列的“先进先出”的原则,即先进入就绪状态排队的线程被优先分配到处理器资源,随Java语言程序设计教程第第178后才为后进入队列的线程服务。当一个在就绪队列中排队的线程被分配到处理器资源而进入运行状态之后,这个线程就称为是被“调度”或线程调度管理器选中了。线程调度管理器负责线程排队和处理器在线程间的分配,一般都有一个精心设计的线程调度算法。在Java系统中,线程调度采用优先级基础上的“先到先服务”原则。11.1.4线程组线程组是一个Java特有的概念,在Java中,线程组是类ThreadGroup的对象,每个线程都隶属于惟一一个线程组,这个线程组在线程创建时指定并在线程的整个生命期内都不能更改。用户可以通过调用包含ThreadGroup类型参数的Thread类构造函数来指定线程所属的线程组。Java语言规定,只能在创建线程时设置线程所属的线程组。可以在创建线程时显式地制定线程组,此时需要采用下述三种构造方法之一:1)Thread(ThreadGroup,Runnable)2)Thread(ThreadGroup,String)3)Thread(ThreadGroup,Runnable,String)若没有指定,则线程默认地隶属于名为system的系统线程组。在Java中,除了预建的系统线程组外,所有线程组都必须显式创建。例如,下面的语句创建了一个名为myThreadGroup的线程组:ThreadGroupmyThreadGroup=newThreadGroup(“myGroupofThreads”)在Java中,除系统线程组外的每个线程组又隶属于另一个线程组,用户可以在创建线程组时指定其所隶属的线程组,若没有指定,则默认地隶属于系统线程组。这样,所有线程组组成了一棵以系统线程组为根的树。Java允许对一个线程组中的所有线程同时进行操作,比如可以通过调用线程组的相应方法来设置其中所有线程的优先级,也可以启动或阻塞其中的所有线程。Java的线程组机制的另一个重要作用是线程安全。线程组机制允许通过分组来区分有不同安全特性的线程,对不同组的线程进行不同的处理,还可以通过线程组的分层结构来支持不对等安全措施的采用。Java的ThreadGroup类提供了大量的方法来方便对线程组树中的每一个线程组以及线程组中的每一个线程进行操作。11.2Java的Thread类和Runnable接口Java中编程实现多线程应用有两种途径:一种是创建用户自己的线程子类,一种是在用户自己的类中实现Runnable接口。11.2.1Thread类Thread类是一个具体的类,该类封装了线程的属性和行为。1.构造函数Thread类的构造函数有多个,比较常用的有如下几个:1)publicThread();这个方法创建了一个默认的线程类的对象。2)publicThread(Runnabletarget);第第第11章Java多线程程序179这个方法在上一个构造函数的基础上,利用一个实现了Runnable接口参数对象Target中所定义的run()方法,以便初始化或覆盖新创建的线程对象的run()方法。3)publicThread(Stringname);这个方法在第一个构造函数创建一个线程的基础上,利用一个String类的对象name为所创建的线程对象指定了一个字符串名称供以后使用。4)publicThread(ThreadGroupgroup,Runnabletarget);这个方法在第二个构造函数创建一个初始化了run()方法的线程基础上,利用给出的ThreadGroup类的对象为所创建的线程指定了所属的线程组。5)publicThread(ThreadGroupgroup,Stringname);这个方法在第三个构造函数创建了一个指定了一个字符串名称的线程对象的基础上,利用给出的ThreadGroup类的对象为所创建的线程指定了所属的线程组。6)publicThread(ThreadGroupgroup,Runnabletarget,Stringname);这个方法综合了上面提到的几种情况,创建了一个属于group的线程组,用target对象中的run()方法初始化了本线程中的run()方法,同时还为线程指定了一个字符串名。利用构造函数创建新线程对象之后,这个对象中的有关数据即被初始化,从而进入线程生命周期的第一个阶段-新建阶段。2.线程优先级Thread类有三个有关线程优先级的静态常量:publicstaticfinalintMAX_PRIORITYpublicstaticfinalintMIN_PRIORITYpublicstaticfinalintNORM_PRIORITY其中MAX_PRIORITY代表最高优先级,通常是10;NORM_PRIORITY代表普通优先级,通常是5;MIN_PRIORITY代表最低优先级,通常是1。对应一个新建线程,系统会根据如下的原则为其定义的优先级:1)新建线程将继承创建它的父线程的优先级。父线程是指执行创建新线程对象语句的线程,它可能是程序的主线程,也可能是某一个用户自定义的线程。2)一般情况下,主线程具有普通优先级。另外,用户可以通过调用Thread类的方法setPriority()来修改系统自动设定的线程优先级,使之符合程序的特定需要:publicfinalvoidsetPriority(intnewPriority)3.其他主要方法1)启动线程的start()方法:publicvoidstart()start()方法将启动线程对象,使之从新建状态转入到就绪状态并进入就绪队列排队。2)定义线程操作的run()方法:publicvoidrun()Thread类的run()方法是用来定义线程对象被调用之后所执行的操作,都是系统自动调用而用户程序不得引用的方法。系统的Thread类中,run()方法没有具体内容,所以用户程序需要创建自己的Thread类的子类,并定义新的run()方法来覆盖原来的run()方法。run()方法将运行线程,使之从就绪队列状态转入到运行状态。3)使线程暂时休眠的sleep()方法:Java语言程序设计教程第第180publicstaticvoidsleep(longmillis)throwsInterruptedException//millis是毫秒为单位的休眠时间线程的调度执行是按照其优先级的高低顺序进行的,当高级线程未完成,即未死亡时,低级线程没有机会获得处理器。有时,优先级高的线程需要优先级低的线程做一些工作来配合它,或者优先级高的线程需要完成一些费时的操作,此时优先级高的线程应该让出处理器,使优先级低的线程有机会执行。为达到这个目的,优先级高的线程可以在它的run()方法中调用sleep()方法来使自己放弃处理器资源,休眠一段时间。休眠时间的长短由sleep()方法的参数决定。进入休眠的线程仍处于活动状态,但不被调度运行,直到休眠期满。它可以被另一个线程用中断唤醒。如果被另一个线程唤醒,则会抛
本文标题:94第11章 Java多线程
链接地址:https://www.777doc.com/doc-4343685 .html