線程的中斷

任務(wù)和線程的啟動(dòng)很容易姥卢。在大多數(shù)時(shí)候,我們都會(huì)讓它們運(yùn)行直到結(jié)束扶平,或者讓它們自行停止帆离。然而,有時(shí)候我們希望提前結(jié)束任務(wù)或線程结澄,或許是因?yàn)橛脩羧∠瞬僮鞲绻龋蛘邞?yīng)用程序需要被快速閉要使任務(wù)和線程能安仝、快速麻献、可靠地停止下來(lái)们妥,并不是一件容易的事。Java的Thread類為我們提供了stop()勉吻,suspend()等停止掛起線程的方法监婶,但是由于安全問(wèn)題目前都已被棄用。Java并沒(méi)有提供一種安全的搶占式方法來(lái)停止線程齿桃,但它提供了中斷(Interruption)惑惶,這是一種協(xié)作機(jī)制,采用協(xié)作式的方式使一個(gè)線程終止另一個(gè)線程的當(dāng)前工作短纵。 這種協(xié)作式的方法是必要的带污,我們很少希望某個(gè)任務(wù)、線程或服務(wù)立即停止踩娘,因?yàn)檫@種立即停止會(huì)使共享的數(shù)據(jù)結(jié)構(gòu)處于不一致的狀態(tài)刮刑。相反,在編寫任務(wù)和服務(wù)時(shí)可以使用一種協(xié)作的方式:當(dāng)需要停止時(shí)养渴,它們首先會(huì)清除當(dāng)前正在執(zhí)行的工作,然后再結(jié)束泛烙。這提供了更好的靈活性理卑,因?yàn)槿蝿?wù)本身的代碼比發(fā)出取消請(qǐng)求的代碼更清楚如何執(zhí)行清除工作。 生命周期結(jié)束(End-of-Lifecycle)的問(wèn)題會(huì)使任務(wù)蔽氨、服務(wù)以及程序的設(shè)計(jì)和實(shí)現(xiàn)等過(guò)程變得復(fù)雜藐唠,而這個(gè)在程序設(shè)計(jì)中非常重要的要素卻經(jīng)常被忽略。一個(gè)在行為良好的軟件與勉強(qiáng)運(yùn)行的軟件之間的最主要區(qū)別就是鹉究,行為良好的軟件能很完善地處理失敗宇立、關(guān)閉和取消等過(guò)程。如何設(shè)計(jì)一種協(xié)作機(jī)制自赔,讓線程可以安全的中斷呢妈嘹?我們可以設(shè)置一個(gè)取消標(biāo)志,在工作線程會(huì)被中斷的地方去檢查這個(gè)標(biāo)志绍妨,當(dāng)檢查到這個(gè)中斷標(biāo)志被設(shè)置為已取消時(shí)润脸,工作線程便開(kāi)始做取消工作柬脸。

public class CancelableThread implements Runnable {
    
    // 線程取消標(biāo)志,volatile修飾毙驯,保證內(nèi)存可見(jiàn)性
    private volatile boolean isCanceled = false;
    
    @Override
    public void run() {
        while (!isCanceled) {//在工作線程中輪詢檢測(cè)這個(gè)取消標(biāo)志
            System.out.println("The current thread is doing something...");
            System.out.println(Thread.currentThread().getName() + " cancel flag is " + isCanceled);
        }
        // 當(dāng)取消標(biāo)志被設(shè)置為true,執(zhí)行以下代碼倒堕,可以做一些取消工作
        System.out.println(Thread.currentThread().getName() + "The current thread Has been cancelled");
    }

    private void cancel() {
        isCanceled = true;
    }
}
public class MainTest {
    public static void main(String[] args) throws Exception {
        CancelableThread cancelableThread = new CancelableThread();
        new Thread(cancelableThread).start();
        try {
            Thread.sleep(100);
        } finally {
            // 設(shè)置標(biāo)志位為true,來(lái)中斷線程  
      cancelableThread.cancel();
        }
    }
}
  • 打印結(jié)果
Thread-0 cancel flag is false
The current thread is doing something...
Thread-0 cancel flag is false
The current thread is doing something...
Thread-0 cancel flag is false
The current thread is doing something...
Thread-0 cancel flag is false
The current thread is doing something...
Thread-0 cancel flag is true
Thread-0The current thread Has been cancelled 

總結(jié)一下上面的例子爆价,這個(gè)例子有個(gè)缺陷垦巴,run方法如果這樣寫:

@Override
public void run() {
    while(!isCanceled){
        try {
            // 這里相當(dāng)于線程被掛起了
            Thread.sleep(10000);
        }catch (InterruptedException e){
            // 這里用的是isInterrupted方法,標(biāo)志位被清除了
        }
    }
    // 檢測(cè)到中斷標(biāo)志铭段,跳出循環(huán)
}

假如當(dāng)前線程執(zhí)行到了sleep方法魂那,線程被掛起了,無(wú)論標(biāo)志位isCanceled怎么變稠项,根本跳不出循環(huán)涯雅,這樣很可能導(dǎo)致和我們預(yù)期的結(jié)果不一致,所以不建議使用自定義的標(biāo)志位來(lái)控制線程的中斷展运,應(yīng)當(dāng)用下面的方法活逆。

Thread類為我們提供了三個(gè)與線程中斷相關(guān)的方法,來(lái)實(shí)現(xiàn)上述機(jī)制拗胜。這三個(gè)方法分別是:

public void interrupt() {
  //...  省略相關(guān)代碼
  interrupt0();           // Just to set the interrupt flag       
  //...  省略相關(guān)代碼
}
public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}
public boolean isInterrupted() {
    return isInterrupted(false);
}
  • interrupt()方法主要用來(lái)設(shè)置中斷標(biāo)志位蔗候;如果此線程在調(diào)用wait方法或joinsleep方法阻塞時(shí)埂软,那么它的中斷狀態(tài)將被清除(也就是線程不會(huì)理會(huì)中斷標(biāo)志位)锈遥,并且會(huì)收到一個(gè)InterruptedException異常。
  • 靜態(tài)的interrupted()方法用來(lái)測(cè)試當(dāng)前線程是否被中斷勘畔,調(diào)用此方法會(huì)清除線程的中斷狀態(tài)所灸。如果當(dāng)前線程被中斷,則為true炫七;否則為false爬立。
  • isInterrupted()方法用來(lái)測(cè)試當(dāng)前線程是否被中斷,但是不會(huì)清除線程的中斷狀態(tài)万哪。
public class InterruptTest {
    static class InnerThread extends Thread{
        @Override
        public void run() {
            while(!isInterrupted()){
                System.out.println(Thread.currentThread().getName()+" cancle flag is "+isInterrupted());
                try {
                    Thread.sleep(100);
                }catch (InterruptedException e){
                    e.printStackTrace();
                    // 拋出InterruptedException侠驯,中斷標(biāo)志位被清除,再次調(diào)用 interrupt();
                    interrupt();
                }
            }
            System.out.println(Thread.currentThread().getName()+" cancle flag is "+isInterrupted());
        }
    }


    public static void main(String[] args) {
        InnerThread innerThread = new InnerThread();
        innerThread.start();
        try {
            Thread.sleep(1000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        innerThread.interrupt();
//        InnerThread innerThread2 = new InnerThread();
//        innerThread2.start();
//        innerThread2.interrupt();
    }
}
  • 打印結(jié)果
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at InterruptTest$InnerThread.run(InterruptTest.java:13)
Thread-0 cancle flag is true

文章出處(內(nèi)容有所修改):https://www.cnblogs.com/perkins/p/9052139.html

總結(jié)一下:

  1. 一般情況下不建議使用自定義的中斷標(biāo)志位奕巍,原因上面說(shuō)明了
  2. interruptisInterrupted方法配合使用吟策,能解決絕大多數(shù)的中斷業(yè)務(wù)
  3. 在調(diào)用waitsleep的止、join等阻塞或者掛起方法的時(shí)候檩坚,會(huì)拋出中斷異常,為什么這么設(shè)計(jì)?這是為了在線程由于某種原因被掛起后效床,后續(xù)的業(yè)務(wù)如果說(shuō)還沒(méi)執(zhí)行完睹酌,強(qiáng)制中斷線程會(huì)導(dǎo)致不可預(yù)料的后果(比如說(shuō)文件寫入了一半),如果就是想中斷線程剩檀,可以在catch塊中再次調(diào)用interrupt方法
  4. 靜態(tài)的interrupted()isInterrupted()方法都是調(diào)用的private native boolean isInterrupted(boolean ClearInterrupted)根據(jù)傳入的ClearInterrupted的值憋沿,來(lái)判斷是否要清除中斷標(biāo)志位
  5. wait、notify沪猴、notifyAll為什么被定義在了Object類中辐啄?這些都跟鎖有關(guān),鎖是屬于對(duì)象的
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末运嗜,一起剝皮案震驚了整個(gè)濱河市壶辜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌担租,老刑警劉巖砸民,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異奋救,居然都是意外死亡岭参,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門尝艘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)演侯,“玉大人,你說(shuō)我怎么就攤上這事背亥∶爰剩” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵狡汉,是天一觀的道長(zhǎng)娄徊。 經(jīng)常有香客問(wèn)我,道長(zhǎng)轴猎,這世上最難降的妖魔是什么嵌莉? 我笑而不...
    開(kāi)封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮捻脖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘中鼠。我一直安慰自己可婶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布援雇。 她就那樣靜靜地躺著矛渴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上具温,一...
    開(kāi)封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天蚕涤,我揣著相機(jī)與錄音,去河邊找鬼铣猩。 笑死揖铜,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的达皿。 我是一名探鬼主播天吓,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼峦椰!你這毒婦竟也來(lái)了龄寞?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤汤功,失蹤者是張志新(化名)和其女友劉穎物邑,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體滔金,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡色解,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鹦蠕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冒签。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖钟病,靈堂內(nèi)的尸體忽然破棺而出萧恕,到底是詐尸還是另有隱情,我是刑警寧澤肠阱,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布票唆,位于F島的核電站,受9級(jí)特大地震影響屹徘,放射性物質(zhì)發(fā)生泄漏走趋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一噪伊、第九天 我趴在偏房一處隱蔽的房頂上張望簿煌。 院中可真熱鬧,春花似錦鉴吹、人聲如沸姨伟。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)夺荒。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間技扼,已是汗流浹背伍玖。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留剿吻,地道東北人窍箍。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像和橙,于是被迫代替她去往敵國(guó)和親仔燕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354