Java并发编程示例(四):可控的线程中断

在上一节“线程中断”中,我们讲解了如何中断一个正在执行的线程以及为了中断线程,我们必须对Thread动点什么手脚。一般情况下,我们可以使用上一节介绍的中断机制。但是,如果线程实现了一个分配到多个方法中的复杂算法,或者方法调用中有一个递归调用,我们应该使用更好的方式来控制线程的中断。为此,Java提供了InterruptedException异常。当检测到中断请求时,可以抛出此异常,并且在run()方法中捕获。

在本节,我们将使用一个线程查找指定目录及其子目录下文件来演示通过使用InterruptedException异常控制线程中断。

知其然

按照下面所示步骤,实现示例程序。

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.实现run()方法,该方法检查fileName是否一个路径名称。如果是,则调用directoryProcess()方法进行处理。directoryProcess()方法会抛出InterruptedException异常,所以我们需要捕获该异常。代码如下:

复制代码 代码如下:

@Override
public 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());
        }
    }
}

原文中,提到的方法名称为processDirectory()。但是,根据下文的程序,属于笔误。故改正。

4.实现directoryProcess()方法。该方法读取指定目录下的所有文件以及子目录再进行处理。对于每一个目录,该方法进行一个递归调用,来处理参数指定的目录。对于每一个文件,该方法会调用fileProcess()方法。在处理完所有的目录以及文件后,该方法会检查线程是否被中断,这是抛出一个InterruptedException异常。代码如下:

复制代码 代码如下:

/**
 * 处理一个目录
 *
 * @param file 需要处理的目录
 * @throws InterruptedException
 */
private void directoryProcess(File file) throws InterruptedException {
    File[] list = file.listFiles();
    if (null != list) {
        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.实现fileProcess()方法,该方法会比较正在处理的文件和需要查找的文件名。如果文件名称相等,则在控制台打印出一条信息。然后,线程检查是否被中断,如果是,则抛出InterruptedException异常。代码如下:

复制代码 代码如下:

/**
 * 处理的文件
 *
 * @param file 需要处理的文件
 * @throws 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()方法。代码如下:

复制代码 代码如下:

public class Main {
    public static void main(String[] args) {

7.创建并初始化FileSearch对象,然后创建一个Thread对象,来执行该任务。然后,启动该线程。代码如下:

复制代码 代码如下:

FileSearch fileSearch = new FileSearch("C:\\", "autoexec.bat");
Thread thread = new Thread(fileSearch);
thread.start();

8.等待十秒钟,然后中断线程。代码如下:

复制代码 代码如下:

try {
    TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
    e.printStackTrace();
}

thread.interrupt();

9.执行该示例,查看结果。

知其所以然

下面是线程执行的结果。从输出中可以看出,当FileSearch检测到被中断后,如何中止线程执行的。

复制代码 代码如下:

Thread-0 : C:\autoexec.bat
Thread-0: The search has been interrupted

本示例中,我们使用Java的异常来控制线程的中断。当你运行示例时,程序会检测指定目录及其子目录是否包含目标文件。例如,如果输入\b\c\d,程序将会递归调用三次directoryProcess()方法。当线程检测到其被中断,则会抛出InterruptedException异常,无论执行多少次递归调用,程序都会开始执行run()方法。

永无止境

InterruptedException异常一般由Java并发API,例如sleep()方法,抛出。

拿来主义

本文是从 《Java 7 Concurrency Cookbook》 (D瓜哥窃译为 《Java7并发示例集》 )翻译而来,仅作为学习资料使用。没有授权,不得用于任何商业行为。

小有所成

FileSearch类的完整代码

复制代码 代码如下:

package com.diguage.books.concurrencycookbook.chapter1.recipe4;

import java.io.File;

/**
 * Date: 2013-09-18
 * Time: 18:21
 */
public class FileSearch implements Runnable {
    private String initPath;
    private String fileName;

/**
     * 初始化构造函数
     *
     * @param initPath 需要进行查找的目录
     * @param fileName 需要查找的文件名称
     */
    public FileSearch(String initPath, String fileName) {
        this.initPath = initPath;
        this.fileName = fileName;
    }

@Override
    public 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());
            }
        }
    }

/**
     * 处理一个目录
     *
     * @param file 需要处理的目录
     * @throws InterruptedException
     */
    private void directoryProcess(File file) throws InterruptedException {
        File[] list = file.listFiles();
        if (null != list) {
            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();
        }
    }

/**
     * 处理的文件
     *
     * @param file 需要处理的文件
     * @throws 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();
        }
    }
}

Main类的完整代码

复制代码 代码如下:

package com.diguage.books.concurrencycookbook.chapter1.recipe4;

import java.util.concurrent.TimeUnit;

/**
 * Date: 2013-09-18
 * Time: 19:28
 */
public class Main {
    public static void main(String[] args) {
        FileSearch fileSearch = new FileSearch("C:\\", "autoexec.bat");
        Thread thread = new Thread(fileSearch);
        thread.start();

try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

thread.interrupt();
    }
}

时间: 2014-12-03

Java并发编程示例(七):守护线程的创建和运行

Java有一种特殊线程,守护线程,这种线程优先级特别低,只有在同一程序中的其他线程不执行时才会执行. 由于守护线程拥有这些特性,所以,一般用为为程序中的普通线程(也称为用户线程)提供服务.它们一般会有一个无限循环,或用于等待请求服务,或用于执行任务等.它们不可以做任何重要的工作,因为我们不确定他们什么时才能分配到CPU运行时间,而且当没有其他线程执行时,它们就会自动终止.这类线程的一个典型应用就是Java的垃圾回收. 在本节示例中,我们将创建两个线程,一个是普通线程,向队列中写入事件:另外一个是

Java并发编程示例(九):本地线程变量的使用

共享数据是并发程序最关键的特性之一.对于无论是继承Thread类的对象,还是实现Runnable接口的对象,这都是一个非常周重要的方面. 如果创建了一个实现Runnable接口的类的对象,并使用该对象启动了一系列的线程,则所有这些线程共享相同的属性.换句话说,如果一个线程修改了一个属性,则其余所有线程都会受此改变的影响. 有时,我们更希望能在线程内单独使用,而不和其他使用同一对象启动的线程共享.Java并发接口提供了一种很清晰的机制来满足此需求,该机制称为本地线程变量.该机制的性能也非常可观.

Java并发编程示例(十):线程组

对线程分组是Java并发API提供的一个有趣功能.我们可以将一组线程看成一个独立单元,并且可以随意操纵线程组中的线程对象.比如,可以控制一组线程来运行同样的任务,无需关心有多少线程还在运行,还可以使用一次中断调用中断所有线程的执行. Java提供了ThreadGroup类来控制一个线程组.一个线程组可以通过线程对象来创建,也可以由其他线程组来创建,生成一个树形结构的线程. 根据<Effective Java>的说明,不再建议使用ThreadGroup.建议使用Executor. --D瓜哥特此

Java并发编程示例(六):等待线程执行终止

在某些场景下,我们必须等待线程执行完成才能进行下一步工作.例如,某些程序在开始执行之前,需要先初始化一些资源.这时,我们可以启动一个线程专门来做初始化任务,等到线程任务完成后,再去执行其他部分. 为此,Thread类为我们提供了join()方法.当我们使用线程对象调用此方法时,正在掉调用的线程对象将被推迟到被调用对象执行完成后再开始执行. 在本节,示例程序演示等待初始化方法完成后,再去执行其他任务. 知其然 按照下面所示步骤,完成示例程序. 1.创建一个名为DataSourcesLoader的类

Java并发编程示例(八):处理线程的非受检异常

Java语言中,把异常分为两类: 受检异常: 这类异常必须在throws子句中被显式抛出或者在方法内被捕获.例如,IOException异常或ClassNotFoundException异常. 非受检异常: 这类异常不需要显式抛出或捕获.例如,NumberFormatException异常. 当一个受检异常在Thread对象的run()方法中被抛出时,我们必须捕获并处理它,因为run()方法不能抛出异常.而一个非受检异常在Thread对象的run()方法中被抛出时,默认的行为是在控制台打印出堆栈

Java并发编程示例(二):获取和设置线程信息

Thread类包含几个属性,这些属性所表示的信息能帮助我们识别线程.观察其状态.控制其优先级等.这些线程包括如下几种: ID: 该属性表示每个线程的唯一标识: Name: 该属性存储每个线程的名称: Priority: 该属性存储每个Thread对象的优先级.线程优先级分1到10十个级别,1表示最低优先级,10表示最高优先级.并不推荐修改线程的优先级,但是如果确实有这方面的需求,也可以尝试一下. Status: 该属性存储线程的状态.线程共有六种不同的状态:新建(new).运行(runnable

Java并发编程示例(五):线程休眠与恢复

有时,我们需要在指定的时间点中断正在执行的线程.比如,每分钟检查一次传感器状态的线程,其余时间,线程不需要做任何事情.在此期间,线程不需要使用计算机的任何资源.过了这段时间之后,并且当Java虚拟机调度了该线程,则该线程继续执行.为此,你可以使用Thread类的sleeep()方法.该方法以休眠的方式来推迟线程的执行,而且整数类型的参数则指明休眠的毫秒数.当调用sleep()方法,休眠时间结束后,Java虚拟机分配给线程CPU运行时间,线程就会继续执行. 另一种是用sleep()方法的方式是通过

Java并发编程示例(三):线程中断

一个多线程的Java程序,直到所有线程执行完成,整个程序才会退出.(需要注意的是,是所有非后台线程(non-daemon thread)执行完成:如果一个线程执行了System.exit()方法,程序也会退出.)有时,你想中止一个线程的执行,例如你想退出程序,或者你想取消一个正在执行的任务等. Java提供了中断机制,可以让我们显式地中断我们想中止执行的线程.中断机制的一个特征就是我们可以检查线程是否已经被中断,进而决定是否响应中止请求.线程也可以忽略中止请求,继续执行. 在本节,我们所开发的示

Java并发编程示例(一):线程的创建和执行

开门见山 在IT圈里,每当我们谈论并发时,必定会说起在一台计算机上同时运行的一系列线程.如果这台电脑上有多个处理器或者是一个多核处理器,那么这时是实实在在的"同时运行":但是,如果计算机只有一个单核处理器,那么这时的"同时运行"只是表象而已. 所有的现代操作系统全部支持任务的并发执行.你可以边听音乐,边上网看新闻,还不耽误首发电子邮件.我们可以说,这种并发是 进程级并发 .在进程内部,我也可以看到有许许多多的并发任务.我们把运行在一个进程里面的并发任务称 线程. 和

Java并发编程中使用Executors类创建和管理线程的用法

1. 类 Executors Executors类可以看做一个"工具类".援引JDK1.6 API中的介绍:   此包中所定义的 Executor.ExecutorService.ScheduledExecutorService.ThreadFactory 和 Callable 类的工厂和实用方法.此类支持以下各种方法: (1)创建并返回设置有常用配置字符串的 ExecutorService 的方法. (2)创建并返回设置有常用配置字符串的 ScheduledExecutorServi