Java 多線程 interrupt - 線程的中斷

正如中斷二字所表達的意義,在線程運行(run方法)中間打斷它,在Java中,提供了以下3個有關線程中斷的方法

//中斷線程(實例方法)
public void Thread.interrupt();

//判斷線程是否被中斷(實例方法)
public boolean Thread.isInterrupted();

//判斷是否被中斷並清除當前中斷狀態(靜態方法)
public static boolean Thread.interrupted();

當一個線程處於被阻塞狀態或者試圖執行一個阻塞操作時,使用Thread.interrupt()方式中斷該線程,注意此時將會拋出一個InterruptedException的異常,同時中斷狀態將會被複位(由中斷狀態改爲非中斷狀態),如下代碼將演示該過程:

package com.lxk.thread.Interrupt;

import java.util.concurrent.TimeUnit;

/**
 * 阻塞中斷
 * <p>
 * 當一個線程處於被阻塞狀態或者試圖執行一個阻塞操作時,使用Thread.interrupt()方式中斷該線程,
 * 注意此時將會拋出一個InterruptedException的異常,同時中斷狀態將會被複位(由中斷狀態改爲非中斷狀態)
 *
 * @author LiXuekai on 2020/4/30
 */
public class InterruptThread1 {


    /**
     * 阻塞中斷
     */
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread() {
            @Override
            public void run() {
                //while在try中,通過異常中斷就可以退出run循環
                try {
                    while (true) {
                        //當前線程處於阻塞狀態,異常必須捕捉處理,無法往外拋出
                        TimeUnit.SECONDS.sleep(2);
                    }
                } catch (InterruptedException e) {
                    System.out.println("interrupted When Sleep");
                    boolean interrupt = this.isInterrupted();
                    //中斷狀態被複位
                    System.out.println("interrupt:" + interrupt);
                }
            }
        };
        t1.start();
        TimeUnit.SECONDS.sleep(2);
        //中斷處於阻塞狀態的線程
        t1.interrupt();

        /*
         * 輸出結果:
         interrupted When Sleep
         interrupt:false
         */
    }
}

如上述代碼所示,我們創建一個線程,並在線程中調用了sleep方法從而使用線程進入阻塞狀態,啓動線程後,調用線程實例對象的interrupt方法中斷阻塞異常,並拋出InterruptedException異常,此時中斷狀態也將被複位。這裏有些人可能會詫異,爲什麼不用Thread.sleep(2000);而是用TimeUnit.SECONDS.sleep(2);其實原因很簡單,前者使用時並沒有明確的單位說明,而後者非常明確表達秒的單位,事實上後者的內部實現最終還是調用了Thread.sleep(2000);,但爲了編寫的代碼語義更清晰,建議使用TimeUnit.SECONDS.sleep(2);的方式,注意TimeUnit是個枚舉類型。ok~,除了阻塞中斷的情景,我們還可能會遇到處於運行期且非阻塞的狀態的線程,這種情況下,直接調用Thread.interrupt()中斷線程是不會得到任響應的,如下代碼,將無法中斷非阻塞狀態下的線程:

package com.lxk.thread.Interrupt;

import java.util.concurrent.TimeUnit;

/**
 * 處於運行期且非阻塞的狀態的線程
 * 直接調用Thread.interrupt()中斷線程是不會得到任響應的
 *
 * @author LiXuekai on 2020/4/30
 */
public class InterruptThread2 {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("未被中斷");
                }
            }
        };
        t1.start();
        TimeUnit.SECONDS.sleep(2);
        t1.interrupt();

        /*
         * 輸出結果(無限執行):
         未被中斷
         未被中斷
         未被中斷
         ......
         */

        // 雖然我們調用了interrupt方法,但線程t1並未被中斷,因爲處於非阻塞狀態的線程需要我們手動進行中斷檢測並結束程序
    }
}

改進後代碼如下:

package com.lxk.thread.Interrupt;

import java.util.concurrent.TimeUnit;

/**
 * 當線程處於運行狀態時,我們也可調用實例方法interrupt()進行線程中斷,
 * 但同時必須手動判斷中斷狀態,並編寫中斷線程的代碼(其實就是結束run方法體的代碼)
 * @author LiXuekai on 2020/4/30
 */
public class InterruptThread3 {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread() {
            @Override
            public void run() {
                while (true) {
                    //判斷當前線程是否被中斷
                    if (this.isInterrupted()) {
                        System.out.println("線程中斷");
                        break;
                    }
                }

                System.out.println("已跳出循環,線程中斷!");
            }
        };
        t1.start();
        TimeUnit.SECONDS.sleep(2);
        t1.interrupt();

        /*
         * 輸出結果:
         線程中斷
         已跳出循環,線程中斷!
         */
    }
}

是的,我們在代碼中使用了實例方法isInterrupted判斷線程是否已被中斷,如果被中斷將跳出循環以此結束線程,注意非阻塞狀態調用interrupt()並不會導致中斷狀態重置。

綜合所述,可以簡單總結一下中斷兩種情況,一種是當線程處於阻塞狀態或者試圖執行一個阻塞操作時,我們可以使用實例方法interrupt()進行線程中斷,執行中斷操作後將會拋出interruptException異常(該異常必須捕捉無法向外拋出)並將中斷狀態復位,另外一種是當線程處於運行狀態時,我們也可調用實例方法interrupt()進行線程中斷,但同時必須手動判斷中斷狀態,並編寫中斷線程的代碼(其實就是結束run方法體的代碼)。有時我們在編碼時可能需要兼顧以上兩種情況,那麼就可以如下編寫:

package com.lxk.thread.Interrupt;

import java.util.concurrent.TimeUnit;

/**
 * 一種是當線程處於阻塞狀態或者試圖執行一個阻塞操作時,我們可以使用實例方法interrupt()進行線程中斷,執行中斷操作後將會拋出interruptException異常(該異常必須捕捉無法向外拋出)並將中斷狀態復位,
 * 另外一種是當線程處於運行狀態時,我們也可調用實例方法interrupt()進行線程中斷,但同時必須手動判斷中斷狀態,並編寫中斷線程的代碼(其實就是結束run方法體的代碼)。
 * 有時我們在編碼時可能需要兼顧以上兩種情況,那麼就可以如下編寫
 *
 * @author LiXuekai on 2020/4/30
 */
public class InterruptThread4 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread() {
            @Override
            public void run() {
                try {
                    //判斷當前線程是否已中斷,注意interrupted方法是靜態的,執行後會對中斷狀態進行復位
                    while (!Thread.interrupted()) {
                        TimeUnit.SECONDS.sleep(2);
                    }
                } catch (InterruptedException e) {
                    System.out.println(e);
                }
            }
        };
        t1.start();
        TimeUnit.SECONDS.sleep(2);
        t1.interrupt();

        /*
         * 輸出結果:
         java.lang.InterruptedException: sleep interrupted
         */
    }
}

Java線程的中斷 != stop 線程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章