Thread interrupt() 執行緒中斷的詳細說明

zhao發表於2020-12-07

GitHub原始碼地址

原創宣告:作者:Arnold.zhao 部落格園地址:https://www.cnblogs.com/zh94

一個執行緒不應該由其他執行緒來強制中斷或停止,而是應該由執行緒自己自行停止。所以,Thread.stop, Thread.suspend, Thread.resume 都已經被廢棄了。Java Thread.interrupt()方法所提供的執行緒中斷,實際就是從執行緒外界,修改執行緒內部的一個標誌變數,或者讓執行緒中的一些阻塞方法,丟擲InterruptedException。以此”通知“執行緒去做一些事情, 至於做什麼,做不做,實際完全是由執行緒內的業務程式碼自己決定的。不過一般都是釋放資源並結束執行緒。

基本概念

public static void basic() {

        Thread testThread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println();
            }
        });

        testThread.interrupt();         //是給執行緒設定中斷標誌;  其作用是中斷此執行緒(此執行緒不一定是當前執行緒,而是指呼叫該方法的Thread例項所代表的執行緒)

        testThread.isInterrupted();     //只檢測中斷;  作用於此執行緒,即程式碼中呼叫此方法的例項所代表的執行緒;作用是隻測試此執行緒是否被中斷 ,不清除中斷狀態。
        testThread.interrupted();       //是檢測中斷並清除中斷狀態; 作用於當前執行緒(作用是測試當前執行緒是否被中斷(檢查中斷標誌),返回一個boolean並清除中斷狀態,第二次再呼叫時中斷狀態已經被清除,將返回一個false)
        Thread.interrupted();           //同上


        //************************************

        testThread.interrupt(); //設定指定testThread執行緒的狀態為中斷標誌,

        testThread.isInterrupted();// 檢測當前testThread執行緒是否被外界中斷;是則返回true
        testThread.interrupted();//檢測當前testThread執行緒是否收到中斷信令,收到信令則返回true且清除中斷狀態,重新變更為false;
        Thread.interrupted();//靜態方法,與testThread.interrupted()一樣,(檢測當前testThread執行緒是否被中斷,如果被中斷則返回true且清除中斷狀態,重新變更為未中斷狀態;) 作用於當前被執行執行緒,由於testThread內部執行緒在執行的時候,是無法獲取testThread引用的,所以如果想檢測當前自己的執行緒是否被中斷且清除中斷狀態,則可以使用Thread.interrupted()方法;


        //如上,其實關於執行緒中斷一共也就上述三個方法,其中interrupt()和isInterrupted() 是執行緒例項方法,interrupted()則是執行緒的靜態方法;
        //isInterrupted()是執行緒例項方法,所以,執行緒內部執行程式碼中是無法獲取testThread的引用的所以無法執行例項方法isInterrupted();
        //但其實,我們可以通過線上程內部執行程式碼中使用 Thread.currentThread()獲取當前執行緒的例項,此時使用Thread.currentThread().isInterrupted() 的方式來呼叫isInterrupted()方法;等價於testThread.isInterrupted();
        //等價與:執行緒外部做檢測用:testThread.isInterrupted(); 執行緒內部做檢測用:Thread.currentThread().isInterrupted()

    }

執行緒中斷驗證

/**
     * 驗證一般情況下使用interrupt() 中斷執行執行緒的例子
     */
    public static void threadStopTest() {
        Thread testThread = new Thread(new Runnable() {
            @Override
            public void run() {
                //第一種情況:檢測執行緒是否收到中斷信令,收到則返回true,並清除當前的執行緒狀態,重新變更為未中斷;
              /*  while (!Thread.interrupted()) {
                    System.out.println("執行緒內程式碼執行");
                }
                //此時再檢測當前該執行緒是否收到外界中斷信令,得到結果為false,因為使用Thread.interrupted(),在收到中斷信令後,會清除當前的執行緒狀態,所以此處進行判斷時則返回結果為false,執行緒狀態未收到中斷信令
                System.out.println(Thread.currentThread().isInterrupted());
                System.out.println(Thread.currentThread().isInterrupted());
*/
                //第二種情況:檢測執行緒是否收到中斷信令,收到則返回true,只是檢測當前是否收到中斷信令,不清除當前的執行緒狀態,
                while (!Thread.currentThread().isInterrupted()) {
                    System.out.println("執行緒內程式碼執行");
                }
                //此時檢測當前該執行緒是否收到外界中斷信令,true表示收到,此處獲取結果為 true
                System.out.println(Thread.currentThread().isInterrupted()); //true
                System.out.println(Thread.currentThread().isInterrupted()); //true
                //執行緒被中斷後執行該程式碼塊,進行回收工作
                System.out.println("執行緒收到外部中斷通知,已進行執行緒內部回收,中斷完成");
                /*while (true) {

                }*/
            }
        });
        testThread.start();
        try {
            Thread.sleep(5000);
            //等待5秒後 發出中斷訊號,通知testThread執行緒進行中斷
            testThread.interrupt();
            //判斷當前該執行緒是否中斷完成
            boolean flag = true;
            int index = 0;
            Thread.sleep(1000);
            while (flag) {
                //獲取指定執行緒是否收到中斷訊號,返回true表示執行緒已經收到中斷訊號,但執行緒正在執行,處理中;或者是已經收到了中斷信令,但是選擇了不中斷繼續執行;
                // 如果返回false則存在兩種情況
                //1、是當前該執行緒已經執行完畢,完成中斷;由於此時執行緒已經執行完成了,那麼此處再獲取該執行緒的信令時則返回為false,
                //2、該執行緒沒有完成中斷,但是該執行緒程式碼內部使用了Thread.interrupted() 清除了執行緒的信令狀態,此時則也是返回結果為false,
                System.out.println("檢測執行緒的中斷訊號:" + testThread.isInterrupted());
                //迴圈檢測10秒鐘,10秒後則跳出迴圈
                Thread.sleep(1000);
                index++;
                if (index == 10) {
                    //停止檢測
                    flag = false;
                }
            }
            if (!testThread.isInterrupted()) {
                //TODO: testThread執行緒中斷完成,則執行該程式碼塊
                System.out.println("外部檢測testThread中斷完成");
            } else {
                //TODO: 否則,則執行另外程式碼塊
                System.out.println("外部檢測testThread中斷失敗");
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

原創宣告:作者:Arnold.zhao 部落格園地址:https://www.cnblogs.com/zh94

驗證執行緒中斷,丟擲InterruptedException異常的情況

/**
     * 驗證執行緒中斷,丟擲InterruptedException異常的情況;
     */
    public static void threadStopTest2() {

        /**
         * 當外部呼叫對應執行緒進行中斷的信令時,如果此時該執行執行緒處於被阻塞狀態,如;Thread.sleep(),Object.wait(),BlockingQueue#put、BlockingQueue#take 等
         * 那麼此時通過呼叫當前執行緒物件的interrupt方法觸發這些函式丟擲InterruptedException異常。
         * 當一個函式丟擲InterruptedException異常時,表示這個方法阻塞的時間太久了,外部應用不想等它執行結束了。
         * 當你的捕獲到一個InterruptedException異常後,亦可以處理它,或者向上丟擲。
         *
         * 丟擲時要注意???:當你捕獲到InterruptedException異常後,當前執行緒的中斷狀態已經被修改為false;
         * 此時你若能夠處理中斷,正常結束執行緒,則不用理會該值;但如果你繼續向上拋InterruptedException異常,你需要再次呼叫interrupt方法,將當前執行緒的中斷狀態設為true。
         *
         */

        Thread testThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //外部呼叫信令,要中斷該執行緒時,如果此時執行緒正在休眠或者阻塞中,則將會丟擲異常
                    Thread.sleep(6000);
                } catch (InterruptedException e) {
                    //第一種情況:進入異常捕獲,此處捕獲到異常,則說明當前該執行緒被外界要求進行中斷,此時我們可以選擇中斷該執行緒;那麼此時後續的while迴圈則將不會被執行,執行緒執行完畢,則結束;

                    /*if (1 > 0) {
                        return;
                    }
                    */

                    //第二種情況:當然,我們也可以選擇收到中斷信令後,不進行執行緒中斷;比如,當前的執行緒的確處於正常的阻塞期間,阻塞完成後,我還是要執行while迴圈的,等到最終while迴圈執行完畢後,才正常的結束執行緒的生命週期;
                    //那麼此時,捕獲到異常後不做任何操作即可;需要注意的是,此時捕獲了InterruptedException異常後,此時的執行緒狀態將會自動被修改為false
                    // (false表示執行緒沒有收到過中斷信令,或者是執行緒已經中斷完成,或者是執行緒使用了Thread.interrupted()清除了信令狀態)沒有收到過中斷信令這個基本是不可能的,只要外部有進行呼叫,則百分百收到信令,除非是在呼叫中斷信令前,獲取了一下執行緒的狀態,此時則肯定是false的;如:先執行,testThread.isInterrupted(),再執行testThread.interrupt();則此時第一個執行的isInterrupted()肯定是false,這個場景意義不大;
                    //1、對於外界來說,收到執行緒的信令狀態是false,則表示該執行緒已經是執行完成了;當然存線上程信令為false是內部執行緒自己進行了轉換,但實際上並沒有停止執行緒執行的情況;
                    //所以一般情況下,按照約定來說,如果內部執行緒收到中斷請求後,此時如果需要繼續執行,不理會外部的中斷信令,那麼此時可以執行:Thread.currentThread().interrupt();重新將內部狀態轉換為true
                    //這樣,外部執行緒在重新檢測當前執行緒的信令狀態時為true時,則知道,內部執行緒已經收到了中斷信令,而不是一直沒有收到中斷信令。

                    //此處為false,捕獲該中斷異常後,將會自動修改執行緒狀態為false,
                    System.out.println("異常" + Thread.currentThread().isInterrupted());
                    //此處由於要繼續執行該執行緒,不執行執行緒中斷,所以重新修改中斷狀態為true;
                    Thread.currentThread().interrupt();
                    //此時獲取結果為true;
                    System.out.println("異常" + Thread.currentThread().isInterrupted());
                }
                while (true) {
                    System.out.println("執行緒內部執行中");
                }

            }
        });
        testThread.start();

        //發出執行緒中斷信令
        testThread.interrupt();
    }

約定

/**
     * 約定:
     * 內部中斷的執行緒,如果需要繼續執行,則必須重新設定信令狀態為true;此時外部呼叫者才會清楚當前執行緒已經收到中斷信令但是還要繼續執行;
     * <p>
     * 什麼情況下,執行緒狀態會自動變更為false?
     * <p>
     * 1、執行緒自動執行完畢後,則狀態將會自動置為 false;
     * 2、執行緒內部使用:Thread.interrupted()方法獲取執行緒狀態時,將會自動清除執行緒狀態,使當前執行緒狀態重新更改為false;
     * 3、執行緒內部如果捕獲了,InterruptedException異常,那麼此時執行緒狀態也會自動修改為false;
     * <p>
     * 所以,
     * 1、如果是使用Thread.interrupted()來獲取執行緒狀態的情況,使用完以後,必須保證執行緒是正常中斷的;如果不能保證,建議使用Thread.currentThread().isInterrupted()來獲取執行緒狀態;isInterrupted()方法只獲取執行緒狀態,不會更改執行緒狀態;
     * 2、對於執行緒內使用try catch 捕獲了InterruptedException異常的情況,則捕獲完以後,一定要做相關操作,而不要只捕獲異常,但是不處理該中斷信令;
     * 當前捕獲到異常後,如果需要中斷,則直接中斷執行緒即可
     * 當前捕獲到異常後,如果不需要中斷,需要繼續執行執行緒,則此時需要執行Thread.currentThread().interrupt();重新更改下自己的執行緒狀態為true,表示當前執行緒需要繼續執行;
     * 當前捕獲到異常後,如果不需要中斷,而是將異常外拋給上層方法進行處理,那麼此時也需要執行Thread.currentThread().interrupt();重新更改下自己的執行緒狀態為true,表示當前執行緒需要繼續執行;
     */

public static void main(String[] args) throws InterruptedException {
//        threadStopTest();


        /*
        Thread.currentThread().interrupt();
        System.out.println(Thread.currentThread().isInterrupted());true
        System.out.println(Thread.currentThread().isInterrupted());true
        System.out.println(Thread.currentThread().isInterrupted());true
        System.out.println(Thread.interrupted());true
        System.out.println(Thread.currentThread().isInterrupted());false
        System.out.println(Thread.interrupted());false
        */
    }

原創宣告:作者:Arnold.zhao 部落格園地址:https://www.cnblogs.com/zh94

相關文章