您好,欢迎访问三七文档
当前位置:首页 > 办公文档 > 招标投标 > 第8章 Java的多线程机制
第8章Java的多线程机制支持多线程是现代操作系统的一大特点。Java语言是跨平台的,无法像C/C++语言一样通过调用系统的API来实现多线程程序,所以它在语言本身加入了对多线程的支持。而且,所有的多线程功能都是以面向对象的方式来实现,学习起来比较简单,控制也很方便。本章就将对Java的多线程机制做一个简明的介绍。8.1线程的概念在操作系统中,通常将进程看作是系统资源的分配单位和独立运行的基本单位。一个任务就是一个进程。比如,现在正在运行IE浏览器,同时还可以打开记事本,系统就会产生两个进程。通俗地说,一个进程既包括了它要执行的指令,也包括了执行指令时所需要的各种系统资源,如CPU、内存、输入输出端口等。不同进程所占用的系统资源相对独立。进程具有动态性、并发性、独立性和异步性等特点。一般情况下,程序员并不需要对进程有更多的了解。本书前面编写的程序一旦被执行,都是独立的进程。它所需要的资源,大多数由操作系统来自动分配,无须程序员操心。线程是一个比较新的概念,在20世纪80年代末才正式被引入。它在提高系统吞吐率、有效利用系统资源、改善用户之间的通讯效率以及发挥多处理机的硬件性能等方面都有显著的作用。因此,线程在现代操作系统中得到了广泛的应用,如Windows、Unix、Linux等都提供了多线程机制。线程是比进程更小的执行单位。某一个进程在执行过程中,可以产生多个线程。每个线程都有自己相对独立的资源(这个和进程非常相似)、生存周期。线程之间可以共享代码和数据、实时通信、进行必要的同步操作。在一个进程中,可以有一个或多个线程的存在。如果程序员不创建线程对象,那么系统至少会创建一个主线程。8.1.1多线程的特点在基于线程(thread-based)的多任务处理环境中,线程是昀小的执行单位。这意味着一个程序可以同时执行两个或者多个任务的功能。例如,一个文本编辑器可以在打印的同时格式化文本。所以,多进程程序处理“大图片”,而多线程程序处理细节问题。多线程程序比多进程程序需要更少的管理费用。进程是重量级的任务,需要分配它们自己独立的地址空间,进程间通信是昂贵和受限的,进程间的转换也是很需要花费的。但是,线程是第8章Java的多线程机制·267·轻量级的选手,它们共享相同的地址空间并且共同分享同一个进程。线程间通信是便宜的,线程间的转换也是低成本的。当Java程序使用多进程任务处理环境时,多进程程序不受Java的控制,而多线程则受Java控制。设计好的多线程,能够帮助程序员写出CPU昀大利用率的高效程序,因为CPU的空闲时间保持昀低。这对Java运行的交互式的网络互连环境是至关重要的,因为空闲时间是公共的。举个例子来说,网络的数据传输速率远低于计算机的处理能力,本地文件系统资源的读写速度远低于CPU的处理能力,当然,用户输入也比计算机慢很多。在传统的单线程环境中,你的程序必须等待每一个这样的任务完成后,才能执行下一步——尽管CPU有很多空闲时间。多线程使你能够获得并充分利用这些空闲时间。进程和线程昀大的区别在于:进程是由操作系统来控制的,而线程是由进程来控制的。所以很多由操作系统完成的工作必须交由程序员完成。前面所写的程序都是单线程的程序,如果需要设计多线程的程序,难度就要大一些。进程都是相互独立,各自享有各自的内存空间。而一个进程中的多个线程是共享内存空间的,这意味着它们可以访问相同的变量和对象,这一方面方便了线程之间的通讯,另一方面又带来了新的问题:多个线程同时访问一个变量可能会出现意想不到的错误。在传统的C/C++、OP等语言中,都是利用操作系统的多线程支持库来完成多线程的程序设计,线程之间的同步、异步、并发、互斥等控制起来比较麻烦(当然好的开发环境也会用类来对这些进行封装)。而Java在语言这一级提供了对多线程的支持,它本身就提供了同步机制,大大方便了用户,降低了设计程序的难度。编制多线程程序,对于程序员而言是一个极大的挑战。尽管Java的线程类已经做得不错了,但还远称不上完美。如果需要编制大型的、要求可靠性很高的多线程程序,还需要程序员花费大量的时间来设计和调试。如果要深入介绍线程控制的每一个细节,足够写出厚厚的一本书。限于篇幅,本章只做一些简明介绍。8.1.2线程的状态Java中用Thread类和它的子类对象来表示线程。一个线程总是处于下面5种状态之一:新建:当创建一个Thread类和它的子类对象后,新产生的线程对象处于新建状态,并获得除CPU外所需的资源。就绪:当处于新建状态的线程被启动后,将进入线程队列等待CPU资源。这时,它已经具备了运行的条件,一旦获得CPU资源,就可以脱离创建它的主线程独立运行了。另外,原来处于阻塞状态的线程结束阻塞状态后,也将进入就绪状态。运行:当一个就绪状态的线程获得CPU时,就进入了运行状态。每个Thread类及其子类对象都有一个run()方法,一旦线程开始运行,就会自动运行该方法。在run()方法中定义了线程所有的操作。阻塞:一个正在运行的线程因为某种特殊的情况,比如,某种资源无法满足,会让出CPU并暂时停止自身的运行,进入阻塞状态。只有当引起阻塞的原因消除时,它才能重新进入就绪状态。Java开发技术大全·268·死亡:不具备继续运行能力的线程处于死亡状态。这一般是由两种情况引起的:一种是run()方法已经运行完毕了,另一种是由其他的线程(一般是主线程)强制终止它。需要指出的是:处于就绪状态的线程是在就绪队列中等待CPU资源的,而一般情况下,就绪队列中会有多个线程。为此,系统会给每一个线程分配一个优先级,优先级高的可以排在较前面的位置,能优先得到CPU资源。对于优先级相同的线程,一般按照先来先服务的原则调度。8.2Thread类在Java中,有两种方法可以创建线程:一种是继承Thread类;另一种是实现Runnable接口。但不管采用哪种方式,都要用到Java类库中的Thread类以及相关方法。8.2.1Thread类的构造方法Thread类的构造方法有多个,各有各的用途,如表8.1所示。表8.1Thread类的构造方法构造方法说明Thread()构造一个线程对象Thread(Runnabletarget)构造一个线程对象,target是被创建线程的目标对象,它实现了Runnable接口中的run()方法Thread(Stringname)用指定字符串为名构造一个线程对象Thread(ThreadGroupgroup,Runnabletarget)在指定线程组中构造一个线程对象,使用目标对象target的run()方法Thread(Runnabletarget,Stringname)用指定字符串为名构造一个线程对象,使用目标对象target的run()方法Thread(ThreadGroupgroup,Runnabletarget,Stringname)在指定线程组中构造一个线程对象,以name作为它的名字,使用目标对象target的run()方法Thread(ThreadGroupgroup,Runnabletarget,Stringname,longstackSize)在指定线程组中构造一个线程对象,以name作为它的名字,使用目标对象target的run()方法,stackSize指定堆栈大小。表中参数Runnable是一个接口,将在8.3.2小节介绍。8.2.2Thread类的常用方法为了能让线程正常运行以及方便程序员对线程的控制,Thread类提供了很多辅助方法。其中,比较常用的方法如表8.2所示。第8章Java的多线程机制·269·表8.2Thread的常用方法方法名说明staticintactiveCount()返回线程组中正在运行的线程数目voidcheckAccess()确定当前运行的线程是否有权限修改线程staticThreadcurrentThread()判断当前哪个线程正在执行voiddestroy()销毁线程,但不收回资源staticvoiddumpStack()显示当前线程中的堆栈信息staticintenumerate(Thread[]tarray)将当前线程组中的线程复制到数组tarray中StringgetName()返回线程的名字intgetPriority()获取线程的优先级ThreadGroupgetThreadGroup()获取线程所属的线程组staticbooleanholdsLock(Objectobj)当前线程被观测者锁定时,返回真voidinterrupt()中断线程staticbooleaninterrupted()测试当前线程是否被中断booleanisAlive()测试线程是否已经正常活动booleanisDaemon()测试线程是否在后台booleanisInterrupted()测试本线程是否被中断voidjoin()等待,直到线程死亡voidjoin(longmillis)等待线程死亡,但昀多只等待millis毫秒voidrun()如果类是使用单独的Runnable对象构造的,将调用Runnable对象的run方法,否则本方法不做任何事情就返回了。如果是子类继承Thread类,请务必实现本方法以覆盖父类的run方法voidsetDaemon(booleanon)将线程标记为后台或者用户线程voidsetName(Stringname)设置线程的名字为namevoidsetPriority(intnewPriority)改变线程的优先级,Java定义了三种级别:Thread.MIN_PRIORITY、Thread.MAX_PRIORITY、和Thread.NORM_PRIORITYstaticvoidsleep(longmillis)正在运行的线程睡眠(暂停),参数millis指定毫秒数staticvoidsleep(longmillis,intnanos)正在运行的线程睡眠(暂停),millis指定毫秒数,nanos指定纳秒数voidstart()启动线程,JVM会自动调用run()方法staticvoidyield()正在运行的线程暂停,同时允许其他的线程运行8.3多线程程序的编写8.2节介绍了Thread类,但是如果要编写多线程程序,是无法直接使用该类的。用户需要继承Thread类或实现Runable接口才行。无论采用哪一种方法,程序员要做的关键性操作有三个:Java开发技术大全·270·定义用户线程的操作,也就是定义用户线程的run()方法。在适当的时候建立用户线程实例,也就是用new来创建对象。启动线程,也就是调用线程对象的start()方法。下面通过几个例子分别来介绍这两种方式实现的多线程程序。8.3.1利用Thread的子类创建线程要创建一个多线程程序,首先要写一个子类继承Thread类,并覆盖其中的run()方法。run()方法中的代码就是这个线程要实现的功能。然后再创建子类对象,这和创建普通类的对象是一样的。昀后调用start()方法启动线程。如果要对线程加以其他的控制,就需要使用Thread类的其他辅助方法。【例8.1】用Thread子类创建多线程程序。先定义一个Thread的子类,该类的run方法只用来输出一些信息。//--------------文件名myThread.java,程序编号8.1------------publicclassmyThreadextendsThread{//定义Thread类的子类privatestaticintcount=0;//这是静态变量,所有线程对象共享//覆盖run方法,实现自己的功能publicvoidrun(){inti;for(i=0;i100;i++){count=count+1;System.out.println(Mynameis+getName()+count=+count);try{sleep(10);//休眠10毫秒,让其他线程有机会运行}catch(InterruptedExceptione){}}}publicmyThr
本文标题:第8章 Java的多线程机制
链接地址:https://www.777doc.com/doc-6271318 .html