Java 7并发编程实战手册(txt+pdf+epub+mobi电子书下载)


发布时间:2020-05-10 20:58:32

点击下载

作者:[西]Javier Fernández González

出版社:人民邮电出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Java 7并发编程实战手册

Java 7并发编程实战手册试读:

前言

、第1章至第5章、附录并对全书所有章节进行全面审校,还负责对原文中的错误与作者进行沟通并加以修正,这里不得不提的一点就是,当你提交的勘误被确认后,英文原书出版社会送出一个免费的电子图书的购买优惠号,真是太感谢了!参与翻译与审校的还有:杨春花、崔毅、俞哲皆、张琬滢、蒋凌锋、魏伟、万国辉等,在此再次深表感谢。

本书章节安排合理,内容承上启下,但是需要边看书边动手做实验,才能充分理解并掌握Java 7 并发API带来的开发技术及新特性。快乐分享,实践出真知,最后,祝大家能够像我一样在阅读中享受本书带来的乐趣!

Read a bit and take it out, then come back read some more.俞黎敏2013年7月1日于广州前言

使用计算机时,可以同时做几件事情:可以一边听音乐,一边使用文字处理软件编辑文档,还可以阅读电子邮件。因为操作系统支持并发任务,从而使得这些工作得以同时进行。并发编程是一种平台和机制供多个任务或程序同时运行,并且互相通讯来交换数据(或者与其他任务进行同步等待)。

Java是一个并发平台,它提供了大量的类来执行Java程序中的并发任务。随着版本的不断更新发展,Java 不断地为程序员增加并发编程的开发功能。本书覆盖了Java 7并发API 中大部分重要而有用的机制,因此,能够直接在应用程序中使用它们,包括下列基本的线程管理:

◆ 线程同步机制

◆通过执行器创建和管理线程

◆ 通过 Fork/Join 框架提高应用程序的性能

◆ 并发编程的数据结构

◆ 根据需要调整一些并发类的默认行为

◆ 测试 Java 并发应用程序本书主要内容

第 1 章,线程管理(Thread Management)将为读者讲解如何通过线程来完成基本的操作。本章将通过基础的范例来讲解线程的创建、执行以及线程的状态管理。

第 2 章,线程同步基础(Basic Thread Synchronization)将为读者讲解如何使用基本的 Java 机制来同步代码。本章将详细阐述 Lock 锁接口和 synchronized 关键字的应用。

第 3 章,线程同步辅助类(Thread Synchronization Utilities)将为读者讲解如何使用 Java 的高级工具类来管理 Java 中的线程同步。本章使用 Java 7 当中的 Phaser 类,来同步被拆分成多个阶段的任务。

第 4 章,线程执行器(Thread Executor)将为读者讲解如何将线程管理委托给执行器(Executor)。执行器将为并发任务负责线程的创建、运行、管理并返回任务的结果。

第 5 章,Fork/Join框架(Fork/Join Framework)将为读者讲解如何使用 Java 7新引入的 Fork/Join框架。它是一种特殊的执行器,用来解决通过分治技术(Divide and Conquer Technique)将任务拆分成多个子任务的问题。

第 6 章,并发集合将为读者讲解如何使用一些由 Java 语言提供的并发数据结构。这些数据结构只能使用在并发编程中,从而避免在程序的实现中采用 synchronized 代码块。

第 7 章,定制并发类(Customizing Concurrency Classes)将为读者讲解如何根据需要来对Java 并发 API 中一些非常有用的类进行定制。

第 8 章,测试并发应用(Testing Concurrent Application)将为读者讲解如何获取 Java 7 并发 API 中最有用的结构的状态信息。读者也将学习如何使用一些免费的工具来调试并发应用程序,比如:Eclipse、NetBeans IDE。同时也将学习用来检测应用程序中是否存在 Bug 的 FindBugs 开源框架。

第 9 章,附加信息(Additional Information)没有包含在本书中,但是可以通过如下链接免费下载:http://www.packtpub.com/sites/default/files/downloads/Additional%20%20Information.pdf。这一章将为读者讲解同步的概念、执行器框架(Executor Framework)和 Fork/Join 框架(Fork/Join Framework)、并发数据结构,以及没有包含在相应章节里的并发对象的监控。

附录,并发编程设计(Concurrent Programming Design)也没有包含在本书当中,但是它可以通过链接免费下载:http://www.packtpub.com/sites/default/files/downloads/Concurrent %20%20Programming%20Design.pdf

附录将为读者讲解每一位程序员在开发并发应用程序时应当考虑使用的一些技巧。阅读本书的前提

为了阅读本书,首先需要读者有Java编程语言的基础知识,需要知道如何使用一种集成开发环境(Integrated Development Environment,IDE),比如Eclipse或者NetBeans,但是,这个不是必要的先决条件。本书适合人群

如果你是一名Java开发人员,想进一步掌握并发编程和多线程技术,以及挖掘 Java 7并发编程的新特性,那么,本书正适合你。你需要已经熟悉普通的Java开发实践,如果掌握了线程的基本知识,那么阅读本书将更加得心应手。约定

本书有大量不同的文本风格,以此区别不同的信息。这里有一些范例,以及它们的解释。

代码的文字说明将以如下形式说明“继承了Thread类,并覆盖了run()方法”。代码块的组织形式如下: public Calculator(int number) { this.number=number; }

新术语(New Term)和重要的词(Important Word)将用粗体显示。在屏幕上、菜单里或者对话框上显示的词将用如下形式说明 “在菜单条的File菜单栏下通过New Project来创建一个新项目”。【警示或重要的备注将像这样在框中显示。】【技巧和窍门将像这样显示。】读者反馈

我们一直欢迎读者们的反馈,让我们知道你对本书的想法,比如你喜欢或者不喜欢。读者反馈对我们来讲是非常重要的。发送反馈给我们相当简单,只需要发送电子邮件到feedback@packtpub.com信箱即可,在邮件的主题里提及本书的标题。

如果你是某一方面的技术专家,并且有兴趣编写和出版图书,可以通过http://www. packtpub.com/authors来获得作者指南。客户支持

现在你已是 Packt 图书的读者,我们有大量的方式可以让你的购买利益最大化。下载范例代码

在http://www.PacktPub.com网站上,通过已注册的账号可以下载到所有已经购买的Packt 图书的范例代码。如果你已经在别的地方购买了本书,你可以访问http://www.PacktPub.com/ support 并注册账号,我们将直接通过邮件把代码发送给你。勘误

虽然我们已尽力确保内容的准确性,但错误仍有可能发生。如果你在我们的图书中发现错误,哪怕只是一个错误的文字或代码,如果你将此情况告知我们,我们将不胜感激。这样做,可以帮助其他读者为了这个错误而浪费时间,同时也帮助我们提高这本书后续版本的质量。如果你发现任何错误,请访问链接http://www.packtpub.com/support,选择书的标题,通过链接点击勘误提交表单,然后输入勘误表单的详细内容并提交。一旦核实,你提交的勘误将被接受,并将上传到网站的勘误列表中,或添加到标题下的勘误Errata一节中的现有勘误列表中。通过访问http://www.packtpub.com/support链接,选择书的标题,可以查看现有的勘误表。盗版

所有媒体互联网上的版权材料通过各种媒体进行盗版是一个持续的问题。在 Packt,我们非常重视保护我们的版权和许可。如果你在互联网上遇到以任何形式非法复制和传播我们的作品,请立即向我们提供链接地址或网站名称,这样我们可以立即寻求解决办法。请通过 mailto:copyright@packtpub.com邮箱与我们联系,将怀疑盗版材料的链接告知我们。我们非常感谢能借助你的帮助来保护我们的作者,我们有能力为你带来有价值的内容。问题

对于本书,如果你有任何问题,可以通过mailto:questions@packtpub.com邮箱联系我们,我们将尽最大的努力来解决你的问题。第1章线程管理

本章内容包括:

◆ 线程的创建和运行

◆ 线程信息的获取和设置

◆ 线程的中断

◆ 线程中断的控制

◆ 线程的休眠和恢复

◆ 等待线程的终止

◆ 守护线程的创建和运行

◆ 线程中不可控异常的处理

◆ 线程局部变量的使用

◆ 线程的分组

◆ 线程组中不可控异常的处理

◆ 使用工厂类创建线程1.1 简介

在计算机领域中,我们说的并发(Concurrency)是指一系列任务的同时运行。如果一台电脑有多个处理器或者有一个多核处理器,这个同时性(Simultaneity)是真正意义的并发;但是一台电脑只有一个单核处理器,这个同时性并不是真正的并发。

现代操作系统都允许多任务的并发执行:在听歌的时候,你可以同时阅读电子邮件,也可以同时阅读网页上的信息。这种并发是进程级(Process-Level)并发。但在一个进程内也可以有多个同时进行的任务。这种进程内并发的任务成为线程(Thread)。

与并发相关的另一个概念是并行(Parallelism)。与并发有不同的定义一样,并行也有不同的定义。一些学者认为并发是在单核处理器中使用多线程执行应用,与此同时你看到的程序执行只是表面的;相应的,他们认为并行是在多核处理器中使用多线程执行应用,这里的多核处理器可以是一个多核处理器,也可以是同一台电脑上的多个处理器。另一些学者认为并发执行应用的线程是非顺序执行的,相应的,他们认为并行是使用很多线程去简化问题,这些线程是按预定顺序执行的。

本章提供了很多例子来演示运用Java 7 API进行线程的基本操作。你将看到如何在Java程序里创建和运行线程,如何去控制线程的执行,如何把多个线程进行分组,以及如何去操作分组后的线程单元。1.2 线程的创建和运行

在本章中,我们将学习如何在Java程序中创建和运行线程。在Java语言中,线程跟其他所有元素一样,都是对象(Object)。Java提供了两种方式来创建线程:

◆ 继承Thread类,并且覆盖run()方法。

◆ 创建一个实现Runnable接口的类。使用带参数的Thread构造器来创建Thread对象。这个参数就是实现Runnable接口的类的一个对象。

在本章中,我们将使用第二种方法创建一个简单的程序,这个程序将创建并运行10个线程。每个线程用以计算和打印乘以1~10后的结果,即计算和打印乘法表。准备工作

本节的范例是在Eclipse IDE里完成的。无论你使用Eclipse还是其他的IDE(比如NetBeans),都可以打开这个IDE并且创建一个新的Java工程。范例实现

按照接下来的步骤实现本节的范例。

1.创建一个名为Calculator的类,它实现了Runnable接口。public class Calculator implements Runnable {

2.声明一个名为number的私有(private)int属性。编写这个类的一个构造器,用来为属性number设置值。private int number;public Calculator(int number) { this.number=number;}

3.编写run()方法。这个方法用来执行我们创建的线程的指令,本范例中它将对指定的数字进行乘法表运算。@Overridepublic void run() { for (int i=1; i<=10; i++){ System.out.printf("%s: %d * %d = %d\n",Thread. currentThread().getName(),number,i,i*number); }}

4.现在编写范例的主类。创建一个名为Main的类,创建的时候同时生成main()方法。public class Main { public static void main(String[] args) {

5.在main()方法中,创建一个执行10次的循环。在每次循环中创建一个Calculator对象,一个Thread对象,这个Thread对象使用刚创建的Calculator对象作为构造器的参数,然后调用刚创建的Thread对象的start()方法。for (int i=1; i<=10; i++){ Calculator calculator=new Calculator(i); Thread thread=new Thread(calculator); thread.start();}

6.运行程序,观察不同的线程是如何并行工作的。工作原理

下面的截图显示了程序的部分运行结果。可以看到我们创建的10个线程的运行情况,它们并行的执行既定任务,并将结果显示出来。

每个Java程序都至少有一个执行线程。当运行程序的时候,JVM将启动这个执行线程来调用程序的main()方法。

当调用Thread对象的start()方法时,另一个执行线程将被创建。因而在我们的程序中,每次调用start()方法时,都会创建一个执行线程。

当一个程序的所有线程都运行完成时,更明确的说,当所有非守护(non-daemon)线程都运行完成的时候,这个Java程序将宣告结束。如果初始线程(执行main()方法的线程)结束了,其余的线程仍将继续执行直到它们运行结束。如果某一个线程调用了System.exit()指令来结束程序的执行,所有的线程都将结束。

对一个实现了Runnable 接口的类来说,创建Thread对象并不会创建一个新的执行线程;同样的,调用它的run()方法,也不会创建一个新的执行线程。只有调用它的start()方法时,才会创建一个新的执行线程。更多信息

在本章简介中提到过还有另一种方法能够创建新的执行线程。编写一个类并继承Thread类,在这个类里覆盖run()方法,然后创建这个类的对象,并且调用start()方法,也会创建一个执行线程。参见

◆ 参见本书1.13节。1.3 线程信息的获取和设置

Thread类有一些保存信息的属性,这些属性可以用来标识线程,显示线程的状态或者控制线程的优先级。

ID:保存了线程的唯一标示符。

Name:保存了线程名称

Priority:保存了线程对象的优先级。线程的优先级是从1到10,其中1是最低优先级;10是最高优先级。我们并不推荐去改变线程的优先级,然而,在需要的时候,也可以这么做。

Status:保存了线程的状态。在Java中,线程的状态有6种:new、runnable、blocked、waiting、time waiting或者 terminated。

在本节,我们将编写程序为10个线程指定名称和优先级,并且输出它们的状态信息直到线程结束。每个线程都将计算一个数字的乘法表。准备工作

本节的范例是在Eclipse IDE里完成的。无论你使用Eclipse还是其他的IDE(比如NetBeans),都可以打开这个IDE并且创建一个新的Java工程。范例实现

按照接下来的步骤实现本节的范例。

1.创建一个名为Calculator的类,它实现了Runnable接口。public class Calculator implements Runnable {

2.声明一个名为number的私有int属性。编写这个类的一个构造器,用来为属性number设置值。private int number;public Calculator(int number) { this.number=number;}

3.编写run()方法。这个方法用来执行我们创建的线程的指令,本范例中它将对指定的数字进行乘法表运算。@Overridepublic void run() { for (int i=1; i<=10; i++){ System.out.printf("%s: %d * %d = %d\n",Thread. currentThread().getName(),number,i,i*number); }}

4.编写范例的主类。创建一个名为Main的类,创建的时候同时生成main()方法。public class Main { public static void main(String[] args) {

5. 创建一个容量为10的线程数组,以用来存储线程;创建一个容量为10的Thread.State数组,以用来存放这10个线程运行时的状态。Thread threads[]=new Thread[10];Thread.State status[]=new Thread.State[10];

6.创建一个容量为10的Calculator对象数组,为每个对象都设置不同的数字,然后使用它们作为Thread构造器的参数来创建10个线程对象。并且将其中5个线程的优先级设置为最高,另外5个线程的优先级设置为最低。for (int i=0; i<10; i++){ threads[i]=new Thread(new Calculator(i)); if ((i%2)==0){ threads[i].setPriority(Thread.MAX_PRIORITY); } else { threads[i].setPriorit87y(Thread.MIN_PRIORITY); } threads[i].setName("Thread "+i);}

7.创建一个PrintWriter对象,用来把线程的状态演变写入到文件中。try (FileWriter file = new FileWriter(".\\data\\log.txt");PrintWriter pw = new PrintWriter(file);){

8.把这10个线程的状态写入文件中。现在线程的状态是NEW。 for (int i=0; i<10; i++){pw.println("Main : Status of Thread "+i+" : " +threads[i].getState()); status[i]=threads[i].getState(); }

9.开始执行10个线程。for (int i=0; i<10; i++){ threads[i].start();}

10.直到10个线程都运行完成,我们就可以查看他们的状态。所有任何一个线程的状态发生了变化,我们就会将它写入到文件中。 boolean finish=false; while (!finish) { for (int i=0; i<10; i++){ if (threads[i].getState()!=status[i]) { writeThreadInfo(pw, threads[i],status[i]); status[i]=threads[i].getState(); } } finish=true; for (int i=0; i<10; i++){finish=finish &&(threads[i].getState()==State.TERMINATED); } }

11.编写writeThreadInfo()方法,用来写下线程的ID、名称、优先级、旧的状态和新的状态。 private static void writeThreadInfo(PrintWriter pw, Threadthread, State state) {pw.printf("Main : Id %d - %s\n",thread.getId(),thread.getName());pw.printf("Main : Priority: %d\n",thread.getPriority());pw.printf("Main : Old State: %s\n",state);pw.printf("Main : New State: %s\n",thread.getState());pw.printf("Main : ************************************\n");}

12.运行这个范例,然后打开log.txt文件来查看10个线程的状态演变。工作原理

下面的截屏是log.txt文件的一部分。在这个文件里,我们可以看到最高优先级的线程比最低优先级的线程结束得早。我们也可以看到每个线程的状态演变。

这个程序的乘法表运算显示在控制台上,每个线程的状态演变记录在log.txt里。这样你可以更清楚地看到线程的演变过程。

Thread类的属性存储了线程的所有信息。JVM使用线程的priority属性来决定某一刻由哪个线程来使用CPU,并且根据线程的情景为它们设置实际状态。

如果没有为线程指定一个名字,JVM将自动给它分配一个名字,格式是Thread-XX,其中XX是一组数字。线程的ID和状态是不允许被修改的,线程类没有提供setId()和setStatus()方法来修改它们。更多信息

通过本节,你已经学会了如何通过Thread对象访问属性信息。但是,也可以通过实现Runnable接口的对象来访问这些属性信息。如果一个线程是以Runnable对象为参数构建的,那么也可以使用Thread类的静态方法currentThread()来访问这个线程对象。

要注意的是,如果使用setPriority()方法设置的优先级不是从1到10这个范围内的值,运行时就会抛出IllegalArgumentException异常。参见

◆ 参见本书1.4节。1.4 线程的中断

如果一个Java程序有不止一个执行线程,当所有线程都运行结束的时候,这个Java程序才能运行结束;更确切地说应该是所有的非守护线程运行结束时,或者其中一个线程调用了System.exit()方法时,这个Java程序才运行结束。如果你想终止一个程序,或者程序的某个用户试图取消线程对象正在运行的任务,就需要结束这个线程。

Java提供了中断机制,我们可以使用它来结束一个线程。这种机制要求线程检查它是否被中断了,然后决定是不是响应这个中断请求。线程允许忽略中断请求并且继续执行。

在本节中,我们将开发程序来创建一个线程,使其运行5秒钟后再通过中断机制强制使其终止。准备工作

本节的范例是在Eclipse IDE里完成的。无论你使用Eclipse还是其他的IDE(比如NetBeans),都可以打开这个IDE并且创建一个新的Java工程。范例实现

按照接下来的步骤实现本节的范例。

1.创建一个名为PrimeGenerator的类,并继承Thread类。public class PrimeGenerator extends Thread{

2.覆盖run()方法,并在方法体内包含一个无限循环。在每次循环中,我们将处理从1开始的连续数。对每个数字,我们将计算它是不是一个质数,如果是的话就打印到控制台。@Overridepublic void run() { long number=1L;while (true) { if (isPrime(number)) { System.out.printf("Number %d is Prime",number);}

3.一个数字处理完后,调用isInterrupted()方法来检查线程是否被中断。如果isInterrupted()返回值是true,就写一个信息并且结束线程的执行。 if (isInterrupted()) { System.out.printf("The Prime Generator has been Interrupted"); return; } number++; }}

4.实现isPrime()方法。isPrime()方法返回的是一个布尔值,如果接收到的参数是一个质数就返回true,否则就返回false。private boolean isPrime(long number) { if (number <=2) { return true; } for (long i=2; i

5.现在我们来实现这个范例的主类Main,并且实现main()方法。public class Main { public static void main(String[] args) {

6.创建PrimeGenerator类的一个对象,并且运行这个线程对象。Thread task=new PrimeGenerator();task.start();

7.等待5秒钟后,中断PrimeGenerator线程。try { Thread.sleep(5000);} catch (InterruptedException e) { e.printStackTrace();}task.interrupt();

8.运行范例并查看结果。工作原理

下面的截屏记录了上述范例的运行结果。通过这个图我们可以看到PrimeGenerator线程打印出的信息,并且看到当它被中断后就运行终止了。

Thread类有一个表明线程被中断与否的属性,它存放的是布尔值。线程的interrupt()方法被调用时,这个属性就会被设置为true。isInterrupted()方法只是返回这个属性的值。更多信息

还有一个方法可以检查线程是否已被中断,即Thread类的静态方法interrupted(),用来检查当前执行的线程是否被中断。

isInterrupted()和interrupted()方法有一个很大的区别。isInterrupted()不能改变interrupted属性的值,但是后者能设置interrupted属性为false。因为interrupted()是一个静态方法,更推荐使用isInterrupted()方法。

像之前提到的,线程可以忽略中断,但并不是预期的行为。1.5 线程中断的控制

通过上一节,你已经学会了如何去中断执行中的线程,也学会了如何在线程对象中去控制这个中断。上一个例子中使用的机制,可以使用在线程很容易被中断的情况下。但是,如果线程实现了复杂的算法并且分布在几个方法中,或者线程里有递归调用的方法,我们就得使用一个更好的机制来控制线程的中断。为了达到这个目的,Java提供了InterruptedException异常。当检查到线程中断的时候,就抛出这个异常,然后在run()中捕获并处理这个异常。

在本节中,我们将实现线程类来完成下面的内容,它在一个文件夹及其子文件夹中寻找一个指定的文件。这个范例将示范如何用InterruptedException异常来控制线程的中断。准备工作

本节的范例是在Eclipse IDE里完成的。无论你使用Eclipse还是其他的IDE(比如NetBeans),都可以打开这个IDE并且创建一个新的Java工程。范例实现

按照接下来的步骤实现本节的范例。

1.创建一个名为FileSearch的类,并且实现Runnable接口。public class FileSearch implements Runnable {

2.声明两个私有属性,一个是我们将要查找的文件名称,另一个是初始文件夹。实现这个类的构造器,用来初始化这两个属性。private String initPath;private String fileName;public FileSearch(String initPath, String fileName) { this.initPath = initPath; this.fileName = fileName;}

3.在FileSearch中实现run()方法。它将检查fileName属性是不是一个目录,如果是,就调用processDirectory()方法。processDirectory()方法会抛出InterruptedException异常,因此必须捕获并处理这个异常。@Overridepublic void run() { File file = new File(initPath); if (file.isDirectory()) { try { directoryProcess(file); } catch (InterruptedException e) { System.out.printf("%s: The search has been interrupted",Thread.currentThread().getName()); } }}

4.实现directoryProcess()方法,这个方法会获取一个文件夹里的所有文件和子文件夹,并进行处理。对于每一个目录,这个方法将递归调用,并且用相应目录名作为传入参数。对于每个文件,这个方法将调用fileProcess()方法。处理完所有的文件和文件夹后,这个方法将检查线程是不是被中断了,如果是,就抛出InterruptedException异常。 private void directoryProcess(File file) throws InterruptedException { File list[] = file.listFiles(); if (list != null) { for (int i = 0; i < list.length; i++) { if (list[i].isDirectory()) { directoryProcess(list[i]); } else { fileProcess(list[i]); } } } if (Thread.interrupted()) { throw new InterruptedException(); }}

5.实现processFile()方法。这个方法将比较当前文件的文件名和要查找的文件名,如果文件名匹配,就将信息打印到控制台。做完比较后,线程将检查是不是被中断了,如果是,它将抛出InterruptedException异常。 private void fileProcess(File file) throws InterruptedException{ if (file.getName().equals(fileName)) { System.out.printf("%s : %s\n",Thread.currentThread().getName() , file.getAbsolutePath()); } if (Thread.interrupted()) { throw new InterruptedException(); }}

6.现在,我们实现这个范例的主类。实现一个包含main()方法的Main类。public class Main { public static void main(String[] args) {

7.创建FileSearch类的一个对象,并用它作为传入参数来创建一个线程对象,然后启动线程执行任务。FileSearch searcher=new FileSearch("C:\\","autoexec.bat");Thread thread=new Thread(searcher);thread.start();

8.等待10秒钟,然后中断线程。 try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } thread.interrupt();}

9.运行这个范例并查看结果。工作原理

下面的截屏记录了范例的运行结果。你可以看到当FileSearch对象检测到它被中断之后就结束了运行。

在本范例中,我们使用了Java异常来控制线程的中断。当运行这个范例时,程序将进入文件夹查找是否包含指定的文件。例如,如果要查找的文件夹目录结构是\b\c\d,这个程序将递归调用processDirectory()方法3次。不管递归调用了多少次,只要线程检测到它已经被中断了,就会立即抛出InterruptedException异常,然后继续执行run()方法。更多信息

与并发API相关的Java方法将会抛出InterruptedException异常,如sleep()方法。参见

◆ 参见本书1.4节。1.6 线程的休眠和恢复

有些时候,你需要在某一个预期的时间中断线程的执行。例如,程序的一个线程每隔一分钟检查一次传感器状态,其余时间什么都不做。在这段空闲时间,线程不占用计算机的任何资源。当它继续执行的CPU时钟来临时,JVM会选中它继续执行。可以通过线程的sleep()方法来达到这个目标。sleep()方法接受整型数值作为参数,以表明线程挂起执行的毫秒数。当线程休眠的时间结束了,JVM会分给它CPU时钟,线程将继续执行它的指令。

sleep()方法的另一种使用方式是通过TimeUnit枚举类元素进行调用。这个方法也使用Thread类的sleep()方法来使当前线程休眠,但是它接收的参数单位是秒,最后会被转化成毫秒。

在本节中,我们将开发程序来完成这样的内容:使用sleep()方法,每间隔一秒就输出实际时间。准备工作

本节的范例是在Eclipse IDE里完成的。无论你使用Eclipse还是其他的IDE(比如NetBeans),都可以打开这个IDE并且创建一个新的Java工程。范例实现

按照接下来的步骤实现本节的范例。

1.创建一个名为FileClock的类,并且实现Runnable接口。public class FileClock implements Runnable {

2.实现run()方法。@Overridepublic void run() {

3.编写一个执行10次的循环。在每个循环中,创建一个Date对象,并把它写入到文件中,然后调用TimeUnit类的SECONDS属性的sleep()方法来挂起线程一秒钟。这个值将让线程休眠大概1秒钟。sleep()方法会抛出InterruptedException异常,我们必须捕获并处理这个异常。最佳实践是,当线程被中断时,释放或者关闭线程正在使用的资源。 for (int i = 0; i < 10; i++) { System.out.printf("%s\n", new Date()); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { System.out.printf("The FileClock has been interrupted"); } }}

4.实现范例的主类。创建一个名为FileMain的类并包含main()方

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载