轻松学Java之多线程_第1页
轻松学Java之多线程_第2页
轻松学Java之多线程_第3页
轻松学Java之多线程_第4页
轻松学Java之多线程_第5页
已阅读5页,还剩35页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第11章 多线程 采用Java中的多线程机制可以使计算机资源得到更好地利用。多线程可以使程序在同一时间内完成很多操作。本章我们将为大家讲解进程与线程的共同点和区别、实现多线程的方法、线程的生命周期、线程的调度、多线程的同步和线程间通信等内容。11.1 线程的基本概念 Java是少数的几种支持“多线程”的语言之一。它可以让不同的程序块一起运行,如此一来就可以让程序运行的更为顺畅,同时也达到了多任务处理的目的。本节我们先来学习有关线程的基本概念。11.1.1 进程和线程 一般来说,进程(Process)就是正在计算机中执行的程序。而线程(Thread)是进程中的某个单一顺序的控制流。线程是进程的细

2、化,它是进程中的实体。进程是应用程序的运行实例,自己享有独立的地址空间。对于多任务操作系统,能够“同时”运行多个进程。但这些是CPU的分时机制在起作用,能够使每个进程都能循环获得自己的CPU时间片。进程(process)本质上是操作系统当前运行的执行程序。至于进程和线程之间的关系,我们可以用图11.1来表示。11.1.1 进程和线程 线程是进程中单一的一个顺序控制流。一个进程可以拥有多个线程,线程是对进程的进一步细化。线程是程序的最小执行单位。也就是说一个程序可以具有同时执行两个,或者多个任务的功能。简单地说,一个程序至少有一个进程,一个进程至少有一个线程。进程在执行过程中拥有独立的内存单元,

3、而对于线程来说,多个线程共享内存,因此极大地提高了程序的运行效率。11.1.2 进程与线程的区别 相对于多进程程序来说,多线程程序的管理花费更小。因为对于进程来说,需要分配给它们自己独立的地址空间,从一个进程转换到另一个进程开销是很大的,并且进程间的通信花费也很大。另一方面,线程共享相同的地址空间并且共同分享同一个进程。线程间能够很方便地进行通信,一个线程转换到另一个线程开销也较小。 在资源调度方面,进程是系统进行资源分配和调度的一个最小单位。线程是进程的进一步细化,是CPU调度和分派的最小单位,线程是比进程更小的能独立运行的基本单位。11.1.3 认识多线程 在传统程序语言中,运行的顺序总是

4、顺着一定的流程来进行的,即使遇到选择、循环语句也是一次只能运行一个程序块。Java多线程打破了这种束缚,使程序可以同时运行多个程序块,使程序运行的效率变得更高。例如,有些包含循环的线程可能要使用比较长的一段时间来运行,此时便可让另一个线程来做其他的处理,如图11.2所示。11.2 创建多线程 使用多线程,首先要学会创建多线程。Java中创建多线程有2种方式:继承Thread类和实现Runnable接口。下面我们一一为大家介绍这两种方式。11.2.1 通过继承Thread类创建多线程 Thread类存放在java.lang类库里,但并不需要加载java.lang类库,因为它会自动加载。run()

5、方法是定义在Thread类里的一个方法,因此把线程的程序代码编写在run()方法内,事实上就是覆盖的操作。我们可以按照图11.4的方式来编写一个多线程。11.2.2 通过实现Runnable接口创建多线程 另一种创建线程类的方式是实现Runnable接口。如果自定义的线程类还要继承其他类,这时就不能采用第一种方式来创建。由于Java语言不支持类的多继承,却可以实现多个接口,所以这种情况可以采用实现Runnable接口的方式创建。通常实现Runnable接口创建线程的语法格式如图11.7所示。 我们发现不管是通过继承还是通过实现接口创建多线程后,程序的输出顺序都发生了改变。11.3 运行线程 上

6、一节中我们学习了如何创建线程,现在我们为大家讲解如何运行线程。本节将分为两个部分来讲,首先是如何启动线程,然后是如何运行多个线程。11.3.1 启动线程 线程创建完成后,通过线程的启动来执行线程。类Thread定义了start()方法用来完成线程的启动。针对两种不同的线程创建方式,下面分别介绍其启动方法。在讲解start()方法之前我们要注意启动线程并不是调用线程类中的run()方法。 在这个程序中,run方法是通过方法调用来执行的,但这并不代表运行了一个线程,这是一个错误的启动线程的方法。如果想正确地启动一个线程,需要调用线程对象的start()方法。1.继承Thread类方式线程的启动 继

7、承Thread类方式的线程启动非常简单,只要创建线程类实例后调用其start()方法即可。启动线程的语法格式如图11.11所示。2.实现Runnable接口线程的启动 实现Runnable接口创建的线程首先转换为Thread类,然后调用Thread类的start()方法启动线程。启动线程的语法格式如图11.13所示。11.3.2 同时运行多个线程 学习了如何启动线程之后,接下来我们就来学习如何同时运行多个线程。 在这里我们要注意,在同时运行多个线程时,运行结果不是唯一的,因为有很多不确定因素。首先先执行那一个线程是不确定的,线程间交替也是不确定的。但是每一个线程都将启动,每一个线程都将结束这是

8、确定的。11.4 线程的生命周期 线程从其创建到死亡具有一个完整的生命周期,在整个生命周期中处于各种状态。线程的状态表明线程当前可以进行的活动。一个生命周期内的线程主要包括创建、就绪、运行、阻塞、死亡状态,如图11.16所示。1.创建状态 在线程类使用new关键字实例化之后且在调用start方法之前,线程处于创建状态。处于创建状态的线程仅仅分配了内存空间,属于生命周期的初始状态。2.就绪状态 在线程调用了start方法后即处于就绪状态。处于就绪状态的线程具备了除CPU之外所有运行所需资源。就绪状态线程排队等待CPU,由系统调度为其分配。3.运行状态 处于就绪状态的线程获得CPU之后即处于运行状

9、态。处于运行状态的线程才开始真正执行线程run方法的内容。4.阻塞/等待状态 处于运行状态的线程如果因为某种原因不能继续执行,则进入阻塞状态。阻塞状态与就绪状态的不同是:就绪状态只是因为缺少CPU而不能执行,而阻塞状态是由于各种原因引起线程不能执行,不仅仅是缺少CPU。引起阻塞的原因解除以后,线程再次转为就绪状态,等待分配CPU运行。5.死亡状态 当线程执行完run ()方法的内容或被强制终止时,线程处于死亡状态,线程的整个生命周期结束。 线程在整个生命周期中始终处于某种状态,从一种状态到另一种状态的转换由线程调度方法实现。下节我们就为大家讲解线程调度的知识。11.5 操作线程的方法 在Thr

10、ead类中,Java定义了许多对线程进行操作的方法,其中主要的方法如表11.1所示。方法方法功能描述功能描述public final String getName()返回线程的名称public final void setName()设定线程名称public final void join()throws InterruptedException等待该线程终止public final native boolean isAlive()判断线程是否在活动,如果是返回true,否则返回falsepublic final void join(long millis)throws Interrupted

11、Exception等待该线程终止,并指定等待的时间。其中millis的单位是毫秒public final void setPriority(int newPriority)修改线程的优先级public final int getPriority()取得线程的优先级public static void sleep(long millis) throws InterruptedException暂停执行当前正在执行的线程并让该线程休眠一段时间。其中millis表示休眠的时间,其单位为毫秒public static void yield()暂停执行当前的线程对象,并执行其他线程public stat

12、ic boolean interrupted()中断线程11.5.1 取得和设置线程的名称 在Thread类中,可以通过getName()方法取得线程的名称,通过setName()方法设置线程的名称。 线程的名称一般在启动线程前设置,但也允许为已经运行的线程设置名称。允许两个Thread对象有相同的名字,但为了清晰,应该尽量避免这种情况的发生。11.5.2 判断线程是否启动 在程序中我们可以通过isAlive()方法来测试线程是否已经启动而且仍然在运行。11.5.3 后台线程与setDaemon()方法 对Java程序来说,只要有一个前台线程在运行,这个进程就不会结束。如果一个进程中只有后台线

13、程在运行,这个进程就会结束。我们前面介绍的线程都是前台线程,那么什么是后台线程呢?如果某个线程在启动之前调用了setDaemon(true)方法,这个线程就变成了后台线程。 从程序和运行结果图中我们可以看到:虽然我们创建了一个无限循环的线程,但因为它是后台线程,因此整个进程在主线程结束时就随之终止了。11.6 线程的调度 处于生命周期中的线程,可以通过调度实现各种状态间的转换。线程的调度是使用各种线程调度方法,如设置优先级方法、睡眠方法、让步方法和等待方法等,对线程进行不同的操作。对于各种线程调度方法,下面分别进行介绍。11.6.1 线程优先级 线程优先级是指线程在被系统调度执行时的优先执行级

14、别。在多线程程序中,往往是多个线程同时等待被调度执行。然而,每个线程的重要程度通常是不一样的,在同等条件下,有些重要线程需要优先执行。在Java语言中,通过调用setPriority()方法为线程设置优先级。优先级用110的数字表示,数字越大,优先级越高。如果没有为线程设置优先级,则线程的优先级默认为5。但是对于子线程来说,它的优先级是和其父线程优先级相同的。 当需要对线程的优先级进行设置时,可以通过调用setPriority方法来设置。setPriority()方法的语法格式如图11.21所示。11.6.2 线程的睡眠方法 在程序设计中,有时需要暂时中断某个线程的运行,在适当的时候再恢复其运

15、行。在Java中,提供sleep()方法来完成这样的工作。 Thread类中提供了sleep()方法,可以让当前线程休眠(停止执行)一定的时间,线程由运行中状态进入阻塞状态,等待停止执行时间到后,线程再进入可运行状态。sleep()方法的语法格式如图11.23所示。11.6.2 线程的睡眠方法 sleep()方法是一个静态的方法,所以sleep()方法不是依赖于某一个对象的,位置比较随意,当在线程中执行sleep()方法,则线程就进入睡眠状态。sleep()方法是可能发生捕获异常的,所以在使用sleep()方法时必须进行异常处理。11.6.3 线程的让步方法 对于正在执行的线程,可以调用yie

16、ld()方法使其重新排队,将CPU让给排在后面的线程,此线程转为就绪态。另外,yield()方法只让步给高优先级或同等优先级的线程,如果后面是低优先级线程,则继续执行此线程。yield没有声明抛出任何异常。yield()方法的语法格式如图11.25所示。11.6.4 线程的等待方法 对于正在执行的线程,可以调用join()方法等待其结束,然后才执行其他程序。join()方法有几种重载形式。其中,不带任何参数是等到执行结束为止,其他重载形式可参考JDK API。join()方法也会抛出可控异常InterruptedException,程序需要对此异常进行处理。join()方法的语法格式如图11.

17、27所示。11.6.5 线程的中断 Interrupt()方法是用来判断当前线程是否中断的方法。如果当前线程被中断,则该方法会返回true,否则会返回false。该方法被定义为static类型的,可以通过使用类名直接调用。11.7 多线程的同步 当多个线程操作同一个共享资源时,比如读写同一个变量,存在着资源竞争的问题。为了解决此类问题,需要使用同步机制。在Java语言中,利用synchronized关键字实现线程同步。线程同步方法定义语法如图11.30所示。11.7 多线程的同步 关于同步还需要说明以下几点: 如果一个同步代码块和非同步代码块同时操纵共享资源时,仍然会造成对共享资源的竞争。 当

18、一个线程开始执行一个同步代码块时,并不意味着这个线程不可以中断。正在执行同步代码块的线程可以执行sleep方法进入休眠,但此时该线程并没有释放同步资源。只是把CPU资源让给其他可运行的线程。 Synchronized不会被继承,如果一个类声明一个方法为synchronized,子类继承该类,这个方法被继承后则不再保持同步,除非子类中的这个方法也使用关键字synchronized修饰。11.8 死锁 在上一节中我们介绍了线程的同步,正是由于线程对象可能拥有同步方法,一旦当前线程还拥有同步锁,那么除非同步锁定被解除,否则其他线程不能访问那个对象。所以一个线程完全可能等候另一个对象,而另一个对象又在等候下一个对象,以此类推。这个等候的队列链最严重的情形就是形成环的状态,即最后那个对象等候的是第一个对象。我们可以用图11.33来表示死锁的过程。 要避免死锁,应该确保在获取多个锁时,在所有的线程中都以相同的顺序获取锁。11.9 线程间通信 Java中提供了wait(),notify

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论