java並沒有提供一種直接中斷或者停止線程的方法,實際上我們說的線程中斷或者取消都是基於一種協作的機制來實現的,所謂協作即調用中斷或者取消方法後,線程會通過檢測某個狀態位來決定是否要退出正在執行的工作,從而來停止線程的執行。應該說線程中斷是沒有一種統一的處理方案的,正確的線程中斷處理方案都需要考慮線程的具體使用的情景。另外線程中斷都不是一個立即執行的動作,前面說了是一種協作機制。
下面說說線程的幾種使用情景:
1)線程中沒有阻塞處理函數,可以使用共享變量機制或者檢查當前線程的中斷狀態。簡單的示例代碼如下:
class t1 extends Thread {
public volatile boolean isInterupted = false;
@Override
public void run() {
while (!isInterupted) {
// do work
System.out.println("t1 run!");
}
System.out.println("t1 exit!");
}
}
class t2 extends Thread {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
// do work
System.out.println("t2 run!");
}
System.out.println("t2 exit!");
}
}
2)線程中有響應中斷的阻塞處理函數,則需要捕獲中斷異常InteruptedException,需要注意的是捕獲這個中斷異常後,中斷狀態位會被清除。若捕獲異常的函數是一個內部函數,那這個時候得想好內部函數的處理機制了,你可以拋出這個中斷異常(注意中斷狀態已經沒有了,外部必須處理這個中斷異常了),你也可以捕獲異常後重新設置中斷狀態位,建議的做法是內部函數不處理異常,直接把異常拋出。示例代碼如下:
class t2 extends Thread {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
work();
} catch (InterruptedException e) {
e.printStackTrace();
this.interrupt();
}
System.out.println("t2 run!");
}
System.out.println("t2 exit!");
}
private void work() throws InterruptedException {
TimeUnit.SECONDS.sleep(5);
}
}
注意上面的實現捕獲異常後,需要重新設置中斷異常位,否則這個線程就沒有辦法正常的中斷了,因爲線程捕獲後中斷位已經被清除了,while循環的條件不會爲真。
3)JDK1.4版本之後,NewIO中提供的channel方法的阻塞處理,是可以響應中斷,只是它收到的不是InterruptedException而是ClosedByInterruptException,這種情況下的處理方式和第2點是一樣的。
4)對於普通的IO操作無法響應中斷的,我們只能通過另外一種方式來中斷線程了,那就是通過關閉IO流的方式來實現。關閉IO流後,它的讀或者寫阻塞操作就觸發IO異常,這時線程可以捕獲該異常,然後終止正在執行的線程,來達到我們中斷線程運行的目的。示例代碼如下:
class t1 extends Thread {
public volatile FileInputStream fis;
@Override
public void run() {
while (true) {
try {
fis.read();
} catch (IOException e) {
e.printStackTrace();
break;
}
}
System.out.println("t1 exit!");
}
}
5)若是通過線程池框架提交的線程,我們可以使用shutdown或者shutdownNow來關閉線程池從而中斷所有線程的執行,當然也可以使用Future的cancle方法來關閉線程,實際上底層調用都是設置了中斷異常標誌來實現的。