正確停止線程

原理介紹:使用interrupt來通知,而不是強制

核心:想要停止線程其實是如何運用interrupt來通知那個線程,以及被停止的線程如何配合。

我們作爲想停止線程的一方,根本沒有能力去強行停止。由於我們想要終端的是其他的線程,這個線程很有可能不是我們來寫的,我們或許對這個正在運行的線程的業務邏輯根本就不熟悉,我們想讓它停止,其實是希望他完成了一系列的保存、交接工作再停止,而不希望它立刻停止,使它陷入一種混亂的狀態。被停止的線程本身,對於自己的業務邏輯是最熟悉的, 所以在設計的時候把停止的權利和步驟交給了被停止線程本身。這個就是一個設計的出發點。

正確的停止方法:interrupt

1、線程通常會在什麼情況下停止呢?

當run方法中的所有代碼都執行完畢。線程中有異常出現,但是沒有捕獲。

2、線程可能被阻塞

3、如果線程在每次迭代後都阻塞

4、如果while裏面放try/catch,會導致中斷失效

package threadcoreknowledge.stopthreads;

/**
 * 描述:     如果while裏面放try/catch,會導致中斷失效
 */
public class CantInterrupt {

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            int num = 0;
            while (num <= 10000 && !Thread.currentThread().isInterrupted()) {
                if (num % 100 == 0) {
                    System.out.println(num + "是100的倍數");
                }
                num++;
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(5000);
        thread.interrupt();
    }
}

原因

雖然我們利用了!Thread.currentThread().isInterrupted()來檢查是不是已經中斷,但是好像毫無效果。之所以會產生這樣的原因, 是因爲java在這幾sleep函數的時候,有這樣的一個理念,一旦響應了中斷 ,於是就會把線程的這個interrupt標記清除。也就是說在剛纔的情況下,由於interrupt標記被清除,所以檢查不到任何別中斷的跡象,導致程序不能退出。

實際開發中兩種最佳實踐

優先選擇傳遞中斷
1、catch了InterruptedExcetion之後的優先選擇:在方法簽名中拋出異常 那麼在run()就會強制try/catch

package threadcoreknowledge.stopthreads;

import threadcoreknowledge.createthreads.ThreadStyle;

/**
 * 描述:     最佳實踐:catch了InterruptedExcetion之後的優先選擇:在方法簽名中拋出異常 那麼在run()就會強制try/catch
 */
public class RightWayStopThreadInProd implements Runnable {

    @Override
    public void run() {
        while (true && !Thread.currentThread().isInterrupted()) {
            System.out.println("go");
            try {
                throwInMethod();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                //保存日誌、停止程序
                System.out.println("保存日誌");
                e.printStackTrace();
            }
        }
    }

    private void throwInMethod() throws InterruptedException {
            Thread.sleep(2000);
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProd());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
}

不想或者無法傳遞:恢復中斷

2、在catch子語句中調用Thread.currentThread().interrupt()來恢復設置中斷狀態,以便於在後續的執行中,依然能夠檢查到剛纔發生了中斷。回到剛纔RightWayStopThreadInProd補上中斷,讓它跳出。

package threadcoreknowledge.stopthreads;

/**
 * 描述:最佳實踐2:在catch子語句中調用Thread.currentThread().interrupt()來恢復設置中斷狀態,以便於在後續的執行中,依然能夠檢查到剛纔發生了中斷
 * 回到剛纔RightWayStopThreadInProd補上中斷,讓它跳出
 */
public class RightWayStopThreadInProd2 implements Runnable {

    @Override
    public void run() {
        while (true) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("Interrupted,程序運行結束");
                break;
            }
            reInterrupt();
        }
    }

    private void reInterrupt() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProd2());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
} 

不應該屏蔽中斷

響應中斷的方法總結列表。

在這裏插入圖片描述在這裏插入圖片描述

總結:爲什麼使用interrupt來停止線程,有什麼好處?

被中斷的線程擁有如何響應中斷的權利,因爲有些線程的某些代碼是非常重要的,我們必須要得等待這些線程處理完之後,或者他們準備好之後,再由他們主動去終止,或者如果他們不想理會我們的中斷,這也是非常ok的。我們不應該魯莽的使用stop方法,而是使用interrupt方法發出一個信號,讓他們自己去處理。 這樣線程更加安全,數據的一致性也得到了保證。

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