線程可能會阻塞或者暫停執行,原因有多種:等待I/O操作結束,等待獲得一個鎖,等待從Thread.sleep方法中醒來,或是等待另一個線程的計算結果。當線程阻塞時,它通常被掛起,並處於某種阻塞狀態(BLOCKED, WAITING或TIMED_WATING)。阻塞操作與執行時間很長的普通操作的差別在於,被阻塞的線程必須等待某個不受它控制的事件發生后才能繼續執行,例如等待I/O操作完成,等待某個鎖變成可用,或者等待外部計算的結束。當某個外部事件發生時,線程被置回RUNNABLE狀態,並可以再次被調度執行。
BlockingQueue的put和take等方法會拋出受檢查異常InterruptedException。當某方法拋出InterruptedException時,表示該方法是一個阻塞方法。如果這個方法被中斷,那么它將努力提前結束阻塞狀態。
Thread提供了interrupt方法,用於中斷線程或者查詢線程是否已經被中斷。每個線程都有一個布爾類型的屬性,表示線程的中斷狀態,當中斷線程時將設置這個狀態。
中斷是一種協作機制。一個線程不能強制其他線程停止正在執行的操作。當線程A中斷B時,A僅僅是要求B在執行到某個可以暫停的地方停止正在執行的操作,前提是B線程願意停止下來。通常使用中斷的情況就是取消某個操作。方法對中斷的響應越高,就越容易及時取消那些執行時間很長的操作。
當在代碼中調用了一個將拋出InterruptedException異常的方法時,你自己的方法也就變成了一個阻塞方法,並且必須處理對中斷的響應。
對於庫代碼來說,有兩種基本選擇:
1、傳遞InrerruptedException。不捕獲該異常,或者捕獲該異常,然后簡單處理好再次拋出異常。
2、恢復中斷。有時候不能拋出異常,如當代碼是Runnable的一部分時。在這些情況下,必須捕獲InteruptedException,並通過調用當前線程上的interrupt方法恢復中斷狀態,這樣在調用棧更高層的代碼中將看到已發了一個中斷,如下面所示
public class TaskRunnable implements Runnable{ BlockingQueue<Task> queue; ... public void run{ try{ processTask(queue.take()); }catch(InterruptedException e){ Thread.currentThread().interrupt(); } } }
在出現InterruptedException時不應該捕獲它但不做出任何響應,導致更高層代碼無法對中斷采取措施,因為中斷信息已經丟失。只有擴展Thread代碼,並且能控制調用棧上所有高層代碼,才能屏蔽中斷。
關於如何處理InterruptedException,可以參考 Java 理論與實踐 處理 InterruptedException 捕捉到它,然后怎么處理它?