首頁 > 軟體

詳解Java如何關閉執行緒以及執行緒池

2022-04-18 13:00:34

前言

這個問題是一個高頻的面試題

而且在印象中是由stop方法執行或者終端中的kill殺死

但是這些方法直接簡單粗暴,很不安全,而且也不推廣

不使用stop的方法

之所以不安全不推廣是因為:

  • stop方法不管執行緒邏輯是否完整,都會終止當前正在執行的執行緒
  • 會破壞其原子邏輯(多執行緒加了鎖之解決資源共用,但是stop會將其所有鎖丟棄,造成混亂)

1. 關閉執行緒

1.1 volatile關鍵字

使用自定義的標誌位決定執行緒的執行情況

具體思路大致如下:設定一個 父執行緒 的狀態變數,以其影響其子執行緒即可

public class test extends Thread {
    //標識執行緒是否結束
    public static boolean thread_stop = true;

    public void stopThread() {
        thread_stop = false;
    }


    public static void main(String[] args) {
        test t = new test();
        t.start();
        System.out.println("Father Thread Start");
        try {
            //先讓執行緒跑起來
            Thread.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //結束執行緒

        //將其狀態變數直接改為false
        //thread_stop = false;

        //呼叫方法改為false(與狀態變數直接修改 一個道理)
        t.stopThread();

        System.out.println("Father Thread end");
    }


    @Override
    public void run() {
        while (thread_stop) {
            System.out.println("Child Thread Start");
        }
        System.out.println("Child Thread end");
    }
}

但是網上說不加volatile是停不下來的,其實是可以停下來的

只不過

加了volatile有幾個好處:

  • volatile可以保證狀態變數為系統記憶體值而不是快取裡值(避免值不一致)
  • volatile 關鍵字能夠是該變數對其他執行緒“可見”,即當主執行緒修改變數並重新整理到主記憶體後,會讓其他執行緒去主記憶體中讀取該變數。當然,volatile並不能保證執行緒的安全性

具體加在狀態變數中的位置如下:

//標識執行緒是否結束
public static volatile boolean thread_stop = true;

之後具體完整的輸出為:

具體完整的輸出為:

Father Thread start
Child Thread Start
Child Thread Start
。。。
。。。
Child Thread Start
Child Thread Start
Father Thread end
Child Thread end

1.2 intrrrupt()方法

不能終止一個正在執行著的執行緒,它只是修改中斷標誌而已

這個方法分為兩種情況:

執行緒處於阻塞:立馬退出阻塞,丟擲InterruptedException異常。通過捕獲這個異常,來讓執行緒退出

執行緒處於非阻塞:處於執行狀態不受影響,僅僅標記了執行緒的中斷為true。在適當的位置中呼叫isInterrupted方法檢視是否被中斷並且退出

public class test extends Thread {
    public static void main(String[] args) {
        test t = new test();
        t.start();
        System.out.println("Father Thread Start");
        try {
            //先讓執行緒跑起來
            Thread.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //結束執行緒

        t.interrupt();

        System.out.println("Father Thread end");
    }


    @Override
    public void run() {
    //分配執行緒的中斷狀態,並且此狀態可以由interrupted()方法生成
        while (!Thread.interrupted()) {
            System.out.println("Child Thread Start");
        }
        System.out.println("Child Thread end");
    }
}

執行結果截圖:

具體完整的輸出為:

Father Thread start
Child Thread Start
Child Thread Start
。。。
。。。
Child Thread Start
Child Thread Start
Father Thread end
Child Thread end

2.關閉執行緒池

優雅的關閉執行緒池:(比如ThreadPoolExecutor類)

可以通過shutdown方法逐步關閉池中的執行緒(溫和安全)

shutdown():拒收新任務,不會立即終止執行緒池。而是要等所有任務快取佇列中的任務都執行完後才終止。

shutdownNow():拒收新任務,立即終止執行緒池。並嘗試打斷正在執行的任務。

並且清空任務快取佇列,返回尚未執行的任務

以下是對兩個執行緒池關閉的方法原始碼進行分析

而且關閉的途中,這兩個方法也不是瞬間立馬關閉,等待關閉的同時,還還呼叫awaitTermination方法來阻塞等待

2.1 shutdownNow()方法

檢視java的原始碼

在try內部結構中

1.檢查其狀態

2.原子性的修改執行緒池的狀態為stop

3.遍歷工作佇列執行緒,呼叫interrupt方法

4.將佇列中還未執行的放到任務佇列

原始碼內部:其邏輯就是修改執行緒池狀態為stop,工作佇列中呼叫interrupt方法

在呼叫shutdownNow方法:

  • 正在執行的執行緒會(getTask返回null)導致執行緒退出。
  • 佇列中讀取的任務會阻塞,丟擲異常之後。工作佇列就會呼叫interrupt方法

2.2 shutdown()方法

同樣也是看java的原始碼

同樣也是4步狀態

1.檢查其狀態

2.修改執行緒池狀態為SHUTDOWN

3.呼叫interruptIdleWorkers方法中斷空閒執行緒(只有加鎖成功的執行緒才會被呼叫interrupt方法)

而正在執行的執行緒是加鎖失敗,不會被中斷

主要通過這個函數去區分判斷

以上就是詳解Java如何關閉執行緒以及執行緒池的詳細內容,更多關於Java關閉執行緒 執行緒池的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com