必須要掌握的 InterruptedException 異常處理
- 2020 年 3 月 2 日
- 筆記
優質文章,及時送達
InterruptedException異常
在了解InterruptedException異常之前應該了解以下的幾個關於執行緒的一些基礎知識。而且得知道什麼時候會拋InterruptedException異常
當阻塞方法收到中斷請求的時候就會拋出InterruptedException異常
執行緒的狀態
執行緒在一定的條件下會發生狀態的改變,下面是執行緒的一些狀態

- 初始(NEW):新建一個執行緒的對象,還未調用start方法
- 運行(RUNNABLE):java執行緒中將已經準備就緒(Ready)和正在運行中(Running)的兩種狀態都統稱為「Runnable」。準備就緒的執行緒會被放在執行緒池中等待被調用
- 阻塞(BLOCKED):是因為某種的原因而放棄了CPU的使用權,暫時的停止了運行。直到執行緒進入準備就緒(Ready)狀態才會有機會轉到運行狀態
- 等待(WAITING):該狀態的執行緒需要等待其他執行緒做出一些特定的動作(通知或者是中斷)
- 超時等待(TIME_WAITING):該狀態和上面的等待不同,他可以在指定的時間內自行返回
- 終止(TERMINATED):執行緒任務執行完畢
而InterruptedException異常從字面意思上就是中斷異常,那麼什麼是中斷呢?學習中斷之前我們先了解一下具體什麼是阻塞
執行緒阻塞
執行緒阻塞通常是指一個執行緒在執行過程中暫停,以等待某個條件的觸發。而什麼情況才會使得執行緒進入阻塞的狀態呢?
- 等待阻塞:運行的執行緒執行wait()方法,該執行緒會釋放佔用的所有資源,JVM會把該執行緒放入「等待池」中。進入這個狀態後,是不能自動喚醒的,必須依靠其他執行緒調用notify()或notifyAll()方法才能被喚醒
- 同步阻塞:運行的執行緒在獲取對象的同步鎖時,若該同步鎖被別的執行緒佔用,則JVM會把該執行緒放入「鎖池」中
- 其他阻塞:運行的執行緒執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該執行緒置為阻塞狀態。當sleep()狀態超時、join()等待執行緒終止或者超時、或者I/O處理完畢時,執行緒重新轉入就緒狀態
執行緒中斷
如果我們有一個運行中的軟體,例如是殺毒軟體正在全盤查殺病毒,此時我們不想讓他殺毒,這時候點擊取消,那麼就是正在中斷一個運行的執行緒。
每一個執行緒都有一個boolean類型的標誌,此標誌意思是當前的請求是否請求中斷,默認為false。當一個執行緒A調用了執行緒B的interrupt方法時,那麼執行緒B的是否請求的中斷標誌變為true。而執行緒B可以調用方法檢測到此標誌的變化。
- 阻塞方法:如果執行緒B調用了阻塞方法,如果是否請求中斷標誌變為了true,那麼它會拋出InterruptedException異常。拋出異常的同時它會將執行緒B的是否請求中斷標誌置為false
- 非阻塞方法:可以通過執行緒B的isInterrupted方法進行檢測是否請求中斷標誌為true還是false,另外還有一個靜態的方法interrupted方法也可以檢測標誌。但是靜態方法它檢測完以後會自動的將是否請求中斷標誌位置為false。例如執行緒A調用了執行緒B的interrupt的方法,那麼如果此時執行緒B中用靜態interrupted方法進行檢測標誌位的變化的話,那麼第一次為true,第二次就為false。下面為具體的例子:
/** * @program: Test * @description: * @author: [email protected] * @create: 2018-07-31 15:43 **/ public class InterrupTest implements Runnable{ public void run(){ try { while (true) { Boolean a = Thread.currentThread().isInterrupted(); System.out.println("in run() - about to sleep for 20 seconds-------" + a); Thread.sleep(20000); System.out.println("in run() - woke up"); } } catch (InterruptedException e) { Thread.currentThread().interrupt();//如果不加上這一句,那麼cd將會都是false,因為在捕捉到InterruptedException異常的時候就會自動的中斷標誌置為了false Boolean c=Thread.interrupted(); Boolean d=Thread.interrupted(); System.out.println("c="+c); System.out.println("d="+d); } } public static void main(String[] args) { InterrupTest si = new InterrupTest(); Thread t = new Thread(si); t.start(); //主執行緒休眠2秒,從而確保剛才啟動的執行緒有機會執行一段時間 try { Thread.sleep(2000); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("in main() - interrupting other thread"); //中斷執行緒t t.interrupt(); System.out.println("in main() - leaving"); } }
列印的參數如下:
in run() - about to sleep for 20 seconds-------false in main() - interrupting other thread in main() - leaving c=true d=false
現在知道執行緒可以檢測到自身的標誌位的變化,但是他只是一個標誌,如果執行緒本身不處理的話,那麼程式還是會執行下去,就好比,老師在學校叮囑要好好學習,具體什麼時候,如何好好學習還是看自身。
因此interrupt() 方法並不能立即中斷執行緒,該方法僅僅告訴執行緒外部已經有中斷請求,至於是否中斷還取決於執行緒自己
InterruptedException異常的處理
簡單的了解了什麼是阻塞和中斷以後,我們就該了解碰到InterruptedException異常該如何處理了。
不要不管不顧
有時候阻塞的方法拋出InterruptedException異常並不合適,例如在Runnable中調用了可中斷的方法,因為你的程式是實現了Runnable介面,然後在重寫Runnable介面的run方法的時候,那麼子類拋出的異常要小於等於父類的異常。
而在Runnable中run方法是沒有拋異常的。所以此時是不能拋出InterruptedException異常。如果此時你只是記錄日誌的話,那麼就是一個不負責任的做法,因為在捕獲InterruptedException異常的時候自動的將是否請求中斷標誌置為了false。
至少在捕獲了InterruptedException異常之後,如果你什麼也不想做,那麼就將標誌重新置為true,以便棧中更高層的程式碼能知道中斷,並且對中斷作出響應。
捕獲到InterruptedException異常後恢復中斷狀態
public class TaskRunner implements Runnable { private BlockingQueue<Task> queue; public TaskRunner(BlockingQueue<Task> queue) { this.queue = queue; } public void run() { try { while (true) { Task task = queue.take(10, TimeUnit.SECONDS); task.execute(); } } catch (InterruptedException e) { // Restore the interrupted status Thread.currentThread().interrupt(); } } }
參考文章
https://blog.csdn.net/sinat_34596644/article/details/51405327 https://blog.csdn.net/u011784767/article/details/51428101 https://www.ibm.com/developerworks/cn/java/j-jtp05236.html https://blog.csdn.net/pange1991/article/details/53860651