中止線程的方法

中止線程的方法

如何中止一個(gè)正在執(zhí)行的線程贝奇?

Thread#stop()

java.lang.Thread#stop強(qiáng)制線程停止執(zhí)行晦闰。

從JDK1.2開始便脊,該API已被棄用胞此,因?yàn)樗?strong>可能導(dǎo)致線程安全問題款票。

Thread#stop()方法通過拋出java.lang.ThreadDeath 異常來達(dá)到中止線程的目的。這會(huì)使線程釋放它持的有全部鎖枷颊,如果之前被鎖保護(hù)的對(duì)象已經(jīng)處于不一致狀態(tài)戳杀,那么這些狀態(tài)將立即對(duì)其它線程可見。當(dāng)其它線程操作一個(gè)損壞的對(duì)象時(shí)夭苗,將會(huì)導(dǎo)致不可預(yù)測(cè)的后果信卡!

而且,默認(rèn)情況下题造,ThreadDeath錯(cuò)誤不會(huì)被打印或通知應(yīng)用程序傍菇。在 java.lang.ThreadGroup中,可以看到這樣一段代碼:

public void uncaughtException(Thread t, Throwable e) {
    if (parent != null) {
        parent.uncaughtException(t, e);
    } else {
        Thread.UncaughtExceptionHandler ueh =
            Thread.getDefaultUncaughtExceptionHandler();
        if (ueh != null) {
            ueh.uncaughtException(t, e);         
        } 
        // ThreadDeath異常被忽略
        else if (!(e instanceof ThreadDeath)) { // 調(diào)用Thread#stop()方法時(shí)界赔,可在此處斷點(diǎn)調(diào)試
            System.err.print("Exception in thread \""
                             + t.getName() + "\" ");
            e.printStackTrace(System.err);
        }
    }
}

這意味著丢习,如果你使用stop()方法中止線程,你的應(yīng)用不會(huì)收到任何通知淮悼。此時(shí)咐低,你的某些對(duì)象可能已經(jīng)處于不一致狀態(tài),然而你對(duì)此一無(wú)所知敛惊。

能否通過捕獲ThreadDeath異常來處理這種情況呢渊鞋?理論上是可以的,但是不推薦這么做。原因有二:

  • ThreadDeath異澄危可能在線程執(zhí)行的任何地方拋出儡湾。那么,所有的同步方法和同步代碼塊都需要對(duì)ThreadDeath異常捕獲處理执俩。
  • catchfinally子句中處理第一個(gè)ThreadDeath異常時(shí)徐钠,可能會(huì)有第二個(gè)ThreadDeath拋出,必須重復(fù)處理直到成功役首。

這樣做的代價(jià)太大尝丐,不現(xiàn)實(shí)。

既然Thread#stop()已經(jīng)被棄用了衡奥,那么有沒有什么替代方案可以中止線程呢爹袁?

基于狀態(tài)的信號(hào)機(jī)制

在大部分使用Thread#stop()的地方都可以用基于狀態(tài)的信號(hào)機(jī)制來替代。

在線程之間共享一個(gè)變量矮固,目標(biāo)線程定期地去檢查這個(gè)變量的狀態(tài)并據(jù)此判斷是否應(yīng)該繼續(xù)執(zhí)行失息,而其它線程就可以通過修改這個(gè)狀態(tài)來中止目標(biāo)線程。

public class StatusBasedSignalStop {

    private static volatile boolean isRunning = true;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            public void run() {
                // 檢查變量的值档址,判斷是否應(yīng)該中止執(zhí)行
                while (isRunning) {
                    System.out.println("線程狀態(tài): " + Thread.currentThread().getState());
                    try {
                        Thread.sleep(1100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 退出執(zhí)行前可以做一些善后工作盹兢,比如資源清理
            }
        });
        thread.start();
        // 3s后修改變量的值,表明目標(biāo)線程應(yīng)該中止了
        Thread.sleep(3000);
        isRunning = false;
        // 休眠1s守伸,再看目標(biāo)線程的狀態(tài)
        Thread.sleep(1000);
        System.out.println("線程狀態(tài): " + thread.getState());
    }
}

程序輸出如下:

線程狀態(tài): RUNNABLE
線程狀態(tài): RUNNABLE
線程狀態(tài): RUNNABLE
線程狀態(tài): TERMINATED

注意:被線程共享的變量必須使用volatile關(guān)鍵字修飾绎秒,或者在同步方法或同步代碼塊中操作,這樣才能保證變量的修改對(duì)其它線程可見尼摹。

Thread#interrupt()

基于狀態(tài)的信號(hào)機(jī)制可以滿足大部分線程中止場(chǎng)景见芹,但是當(dāng)目標(biāo)線程處于長(zhǎng)時(shí)間等待狀態(tài)(比如在一個(gè)條件上等待)時(shí),該機(jī)制無(wú)法使用窘问。此時(shí)辆童,可以嘗試使用Thread#interrupt()去打斷它。

public class InterruptStop {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            public void run() {
                try {
                    // 我想睡好久好久
                    Thread.sleep(Long.MAX_VALUE);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                    // Thread#sleep()拋出InterruptedException會(huì)清除中斷標(biāo)志惠赫,此處再次中斷線程
                    Thread.currentThread().interrupt();
                }
            }
        });
        thread.start();
        // 主線程休眠1s,等待目標(biāo)線程進(jìn)入睡眠
        Thread.sleep(1000);
        // 調(diào)用目標(biāo)線程的interrupt()方法中斷等待
        thread.interrupt();
        // 等待目標(biāo)線程執(zhí)行完畢
        thread.join();
    }
}

運(yùn)行main()函數(shù)故黑,1秒后主線程退出儿咱。這說明Thread#interrupt()成功中止了目標(biāo)線程的睡眠。

然而场晶,并不是所有的等待都被Thread#interrupt中止混埠,當(dāng)目標(biāo)線程處于以下等待場(chǎng)景時(shí):

  • Object#wait();
  • Object#wait(long)诗轻;
  • Object#wait(long, int)钳宪;
  • Thread#join();
  • Thread#join(long);
  • Thread#sleep(long);
  • Thread#sleep(long, int);
  • java.nio.channels.InterruptibleChannel上的阻塞IO操作;
  • java.nio.channels.Selector上的阻塞IO操作;

調(diào)用Thread#interrupt方法會(huì)中斷等待(不同的情景響應(yīng)中斷后線程的中斷標(biāo)志不同)吏颖。其他情況下調(diào)用Thread#interrupt只會(huì)設(shè)置目標(biāo)線程的中斷標(biāo)志搔体,無(wú)法中止等待狀態(tài)。此時(shí)半醉,只能根據(jù)具體場(chǎng)景來分析如何中止等待(比如疚俱,如果線程在Socket上長(zhǎng)時(shí)間等待,可以關(guān)閉Socket來中止等待)缩多。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末呆奕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子衬吆,更是在濱河造成了極大的恐慌梁钾,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逊抡,死亡現(xiàn)場(chǎng)離奇詭異姆泻,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)秦忿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門麦射,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人灯谣,你說我怎么就攤上這事潜秋。” “怎么了胎许?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵峻呛,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我辜窑,道長(zhǎng)钩述,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任穆碎,我火速辦了婚禮牙勘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘所禀。我一直安慰自己方面,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布色徘。 她就那樣靜靜地躺著恭金,像睡著了一般。 火紅的嫁衣襯著肌膚如雪褂策。 梳的紋絲不亂的頭發(fā)上横腿,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天颓屑,我揣著相機(jī)與錄音,去河邊找鬼耿焊。 笑死揪惦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的搀别。 我是一名探鬼主播丹擎,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼歇父!你這毒婦竟也來了蒂培?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤榜苫,失蹤者是張志新(化名)和其女友劉穎护戳,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垂睬,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡媳荒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了驹饺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钳枕。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖赏壹,靈堂內(nèi)的尸體忽然破棺而出鱼炒,到底是詐尸還是另有隱情,我是刑警寧澤蝌借,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布昔瞧,位于F島的核電站,受9級(jí)特大地震影響菩佑,放射性物質(zhì)發(fā)生泄漏自晰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一稍坯、第九天 我趴在偏房一處隱蔽的房頂上張望酬荞。 院中可真熱鬧,春花似錦瞧哟、人聲如沸袜蚕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至遣疯,卻和暖如春雄可,著一層夾襖步出監(jiān)牢的瞬間凿傅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工数苫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留聪舒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓虐急,卻偏偏與公主長(zhǎng)得像箱残,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子止吁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359