漫談並發編程(四):終結任務
使用狀態變量來終結任務
有時我們可以使用一個狀態變量(如布爾值)來終結任務的執行,這種方式非常平和,且提供給你機會在任務終止前做一些操作。如:
public class StateStopTask implements Runnable{
private static volatile boolean isCancled = false;
public void run() {
while(true) {
if(isCancled == true) {
System.out.println("StateStopTask is stop");
return;
}
}
}
public static void main(String []args) {
new Thread(new StateStopThread()).start();
Thread.sleep(3000);
isCancled = true;
}
}
或者直接使用Thread.interrupt()來設置線程的中斷標識,並且在程序中檢測該標識,可以避免狀態變量的定義。
public class StateStopTask implements Runnable{
public void run() {
while(!Thread.currentThread().isInterrupted()) {
System.out.println("StateStopTask is running");
}
System.out.println("StateStopTask is stopping");
}
public static void main(String []args) {
Thread t = new Thread(new StateStopThread()).start();
Thread.sleep(3000);
t.interrupt();
}
}
在阻塞時終結
前面使用狀態變量只能在線程運行到該檢測時才能終止,有時我們需要線程無論在何時何地都能夠被終止,尤其是在阻塞的時候。一個任務進入阻塞狀態,可能有如下原因:
通過調用sleep(milliseconds)使任務進入休眠狀態,在這種情況下,任務在指定的時間內不會運行。你可以通過wait()使線程掛起。直到線程得到了notify()或notifyAll()消息(或者在JavaSE5的java.util.concurrent類庫中等價的signal()或signalAll()消息),線程才會進入就緒狀態。任務在等待某個輸入輸出完成。任務試圖在某個對象上調用其同步控制方法,但是對象鎖不可用,因為另一個任務已經獲取了這個鎖。
中斷
程序在由於sleep或者wait()導致的阻塞情況下,如果被調用interrupt()方法將拋出interruptedExcetion異常,並且中斷狀態將清除。程序在由於IO和鎖的阻塞時,調用interrupt()方法不會拋出interruptedException,但會設置中斷狀態。使用interrupted()可以檢測中斷狀態並且如果正處於中斷則清除中斷狀態,所以在程序中一般使用下面的方式檢測中斷,並做善後處理:
public class InterruptedTask implements Runnable{
public void run() {
while(!Thread.currentThread().interrupted()) {
try {
System.out.println("InterruptedTask running");
Thread.sleep(100);
} catch (InterruptedException e) {
System.out.println("interrupted by InterruptedException ")
}
}
System.out.println("InterruptedTask Interrupted")
}
}
現代java代碼不推薦直接操作Thread對象,轉而盡量通過Executor來執行所有操作。如果你在Executor上調用shutdownNow(),那麼它將發送一個interrupt()調用給它啟動的所有線程。這樣做是常見的,因為一個Executor通常執行同一類任務,所以經常會希望關閉Executor的所有任務。然後,你有時會希望只中斷單一的任務。如果使用Executor,那麼通過調用submit()而不是executor()來啟動任務,就可以持有該任務的上下文,submit()將返回一個泛型Future>,其中有一個未修飾的參數,因為你不能調用get(),使用這個Futrue對象可以調用cancle(),如果傳遞true給cancle(),那麼在這個線程上就會調用interrupt()方法。
一般由於鎖而導致的阻塞無法中斷,但原子鎖(ReentrantLock)支持可中斷的阻塞操作lockInterruptibly()。