java高並發InterruptedException異常引發思考
前言
InterruptedException異常可能沒你想的那麼簡單!
當我們在調用Java對象的wait()方法或者線程的sleep()方法時,需要捕獲並處理InterruptedException異常。如果我們對InterruptedException異常處理不當,則會發生我們意想不到的後果!
程序案例
例如,下面的程序代碼,InterruptedTask類實現瞭Runnable接口,在run()方法中,獲取當前線程的句柄,並在while(true)循環中,通過isInterrupted()方法來檢測當前線程是否被中斷,如果當前線程被中斷就退出while(true)循環,同時,在while(true)循環中,還有一行Thread.sleep(100)代碼,並捕獲瞭InterruptedException異常。
整個代碼如下所示。
package io.binghe.concurrent.lab08; /** * @author binghe * @version 1.0.0 * @description 線程測試中斷 */ public class InterruptedTask implements Runnable{ @Override public void run() { Thread currentThread = Thread.currentThread(); while (true){ if(currentThread.isInterrupted()){ break; } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
上述代碼的本意是通過isInterrupted()方法檢查線程是否被中斷瞭,如果中斷瞭就退出while循環。其他線程通過調用執行線程的interrupt()方法來中斷執行線程,此時會設置執行線程的中斷標志位,從而使currentThread.isInterrupted()返回true,這樣就能夠退出while循環。
這看上去沒啥問題啊!但真的是這樣嗎?我們創建一個InterruptedTest類用於測試,代碼如下所示。
package io.binghe.concurrent.lab08; /** * @author binghe * @version 1.0.0 * @description 測試線程中斷 */ public class InterruptedTest { public static void main(String[] args){ InterruptedTask interruptedTask = new InterruptedTask(); Thread interruptedThread = new Thread(interruptedTask); interruptedThread.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } interruptedThread.interrupt(); } }
我們運行main方法,如下所示。
這竟然跟我們想象的不一樣!不一樣!不一樣!這是為什麼呢?
問題分析
上述代碼明明調用瞭線程的interrupt()方法來中斷線程,但是卻並沒有起到啥作用。原因是線程的run()方法在執行的時候,大部分時間都是阻塞在sleep(100)上,當其他線程通過調用執行線程的interrupt()方法來中斷執行線程時,大概率的會觸發InterruptedException異常,在觸發InterruptedException異常的同時,JVM會同時把線程的中斷標志位清除,所以,這個時候在run()方法中判斷的currentThread.isInterrupted()會返回false,也就不會退出當前while循環瞭。
既然問題分析清除瞭,那如何中斷線程並退出程序呢?
問題解決
正確的處理方式應該是在InterruptedTask類中的run()方法中的while(true)循環中捕獲異常之後重新設置中斷標志位,所以,正確的InterruptedTask類的代碼如下所示。
package io.binghe.concurrent.lab08; /** * @author binghe * @version 1.0.0 * @description 中斷線程測試 */ public class InterruptedTask implements Runnable{ @Override public void run() { Thread currentThread = Thread.currentThread(); while (true){ if(currentThread.isInterrupted()){ break; } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); currentThread.interrupt(); } } } }
可以看到,我們在捕獲InterruptedException異常的catch代碼塊中新增瞭一行代碼。
currentThread.interrupt();
這就使得我們捕獲到InterruptedException異常後,能夠重新設置線程的中斷標志位,從而中斷當前執行的線程。
我們再次運行InterruptedTest類的main方法,如下所示。
總結
處理InterruptedException異常時要小心,如果在調用執行線程的interrupt()方法中斷執行線程時,拋出瞭InterruptedException異常,則在觸發InterruptedException異常的同時,JVM會同時把執行線程的中斷標志位清除,此時調用執行線程的isInterrupted()方法時,會返回false。
此時,正確的處理方式是在執行線程的run()方法中捕獲到InterruptedException異常,並重新設置中斷標志位(也就是在捕獲InterruptedException異常的catch代碼塊中,重新調用當前線程的interrupt()方法)。
寫在最後
最後,附上並發編程需要掌握的核心技能知識圖,祝大傢在學習並發編程時,少走彎路。
以上就是java高並發InterruptedException異常引發思考的詳細內容,更多關於java高並發InterruptedException異常的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- 淺談Thread.sleep()為什麼要拋出中斷異常
- java中斷機制實例講解
- Java線程中斷interrupt的常用方法
- 一篇文章掌握Java Thread的類及其常見方法
- java 兩階段終止線程的正確做法