天天看點

Thread interrupt() 線程中斷的詳細說明

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();
        }
    }           

驗證線程中斷,抛出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
        */
    }