安全優雅地停止執行緒

朱季謙發表於2021-12-05

首先,先丟擲一個問題,該如何安全而優雅地停止執行緒?

這道問題的背後,可以很小,小到只是簡單終止一個Thread執行緒,也可以很大,大到例如Dubbo應用的優雅下線......它們其實都有一個共同之處,即非一刀斷式地暴力停止某個程式或者執行緒,而是能夠實現在終止過程中,有機會去清理資源,跑完剩餘的任務,最後沒有任何資源在執行了,才做最後結束,這才算安全而優雅地停止。

在Java多執行緒當中,停止執行緒的方法,其中,有一個已經過期而不建議使用的方式stop(),它停止執行緒的方式比較簡單粗暴,不保證執行緒的資源正常釋放就直接停止了,也就意味著,可能還有執行緒正在跑,沒有執行完成,就直接終結了,這可能會導致程式出現不確定的狀態,即死鎖狀態。

以stop方式終結執行緒的方法已經過期,即不再建議使用。

那麼,可有其他方式來優雅地結束執行緒執行嗎?

這裡,可以通過interrupt()方法間接實現。

為什麼說是簡接實現呢?

因為執行緒執行interrupt()方法並不會直接就終止執行緒。

接下來,就簡單分析一下,interrupt()是如何實現安全而優雅地終止執行緒的。

首先,當執行執行緒的interrupt()方法後,就會給該執行緒打上一個中斷的標識屬性,該標識屬性原本是false的,但被打上中斷標識後,就會變成true了,這裡有點類似volatitle變數的可見性玩法,通過這樣的可見性變數,我們就可以設定某種狀態,當滿足該狀態時,就可以跳出程式,提前結束。

可以通過isInterrupted()方法獲取到中斷標識屬性的狀態值,若是true,表示該執行緒已經被打上中斷標識,那麼,就可以先清理完資源後,再結束該執行緒。

然而,需要注意一點是,這裡有一個類似的靜態方法,Thread.interrupted(),該方法也可以獲取到執行緒中斷狀態,但遺憾的是,這個interrupted方法在判斷一次執行緒是否中斷後,就會立即對該執行緒的中斷狀態復位,即恢復執行緒到非中斷的狀態。除此之外,宣告丟擲InterruptedException的方法,在丟擲異常前,也會通過虛擬機器將該執行緒的中斷標識狀態清除,然後再丟擲異常,這時再呼叫isInterrupted()方法返回的是false。

這裡以程式碼驗證一下——

public static void main(String[] args) throws InterruptedException {
    Runner one = new Runner();
    Thread countThread = new Thread(one,"CountThread");
    //啟動執行緒
    countThread.start();
    //沉默一秒,先讓執行緒CountThread執行1秒
    TimeUnit.SECONDS.sleep(1);
    //通過interrupt()方法對執行緒countThread設定中斷標識
    countThread.interrupt();
}

private static class Runner implements Runnable {
    private long i;
    private volatile boolean on = true;
    @Override
    public void run() {
        //當countThread執行緒標識中斷時,Thread.currentThread().isInterrupted()返回的是true,即可結束該執行緒,同時,停止資源i++的繼續執行
        while (!Thread.currentThread().isInterrupted()){
            i++;
        }
        System.out.println("Count i = " + i);
    }
}

前邊提到過,interrupt()標識中斷位的玩法,很類似volatitle變數的可見性,反過來,volatitle某種程度上也可以替代interrupt()來判斷執行緒是否需要中斷,類似程式碼如下——

public static void main(String[] args) throws InterruptedException {
    Runner two = new Runner();
    Thread countThread = new Thread(two,"CountThread");
    countThread.start();
    //睡眠1秒
    TimeUnit.SECONDS.sleep(1);
    two.cancel();

}

private static class Runner implements Runnable {
    private long i;
    private volatile boolean on = true;
    @Override
    public void run() {
        while (on){
            i++;
        }
        System.out.println("Count i = " + i);
    }

    public void cancel(){
        on = false;
    }
}

參考《Java併發程式設計的藝術》

相關文章