在上一節「線程中斷」中,我們講解瞭如何中斷一個正在執行的線程以及為了中斷線程,我們必須對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();
}
}