天天看點

InterruptedException異常會對并發程式設計産生哪些影響?

作者:冰河001

寫在前面

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方法,如下所示。

InterruptedException異常會對并發程式設計産生哪些影響?

「這竟然跟我們想象的不一樣!不一樣!不一樣!這是為什麼呢?」

問題分析

上述代碼明明調用了線程的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異常會對并發程式設計産生哪些影響?

總結

「處理InterruptedException異常時要小心,如果在調用執行線程的interrupt()方法中斷執行線程時,抛出了InterruptedException異常,則在觸發InterruptedException異常的同時,JVM會同時把執行線程的中斷标志位清除,此時調用執行線程的isInterrupted()方法時,會傳回false。此時,正确的處理方式是在執行線程的run()方法中捕獲到InterruptedException異常,并重新設定中斷标志位(也就是在捕獲InterruptedException異常的catch代碼塊中,重新調用目前線程的interrupt()方法)。」

寫在最後

如果覺得文章對你有點幫助,請微信搜尋并關注「 冰河技術 」微信公衆号,跟冰河學習高并發程式設計技術。

最後,附上并發程式設計需要掌握的核心技能知識圖,祝大家在學習并發程式設計時,少走彎路。

InterruptedException異常會對并發程式設計産生哪些影響?

繼續閱讀