優雅關閉執行緒池的方案

AnonyStar發表於2020-11-23

  • 我們經常在專案中使用的執行緒池,但是是否關心過執行緒池的關閉呢,可能很多時候直接再專案中直接建立執行緒池讓它一直執行當任務執行結束不在需要了也不去關閉,這其實是存在非常大的風險的,大量的執行緒常駐在後臺對系統資源的佔用是巨大的 ,甚至引發異常。所以在我們平時使用執行緒池時需要注意優雅的關閉,這樣可以保證資源的管控。
  • Java 中和關閉執行緒池相關的方法主要有如下:
    • void shutdown()
    • List<Runnable> shutDownNow
    • boolean awaitTermination
    • boolean isShutDown
    • boolean isTerminated
  • 對於這些方法有著不同的使用和作用,下面我們真的會這些不同的方法做詳細的介紹。

ShutDown

  • shutDown 方法從字面意思我們可以看到是停止關閉的意思,我們先來看下面的一段程式碼,首先我們通過 ThreadPoolExecutor 來建立一個容量是10的無界執行緒池,與 FixedThreadPool 類似的,這裡手動建立可以更好地理解執行緒池的建立。在後我們提交一千個任務執行,再執行 shutdown 方法進行暫停。
 public static void main(String[] args) throws InterruptedException {

        ExecutorService service = new ThreadPoolExecutor(
                10,
                10,
                0L,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>());

        for (int i = 0; i < 1000; i++) {
            service.submit(() ->{
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    System.out.println("接受中斷,不處理~~");
                }
                System.out.println("args = " + Arrays.deepToString(args)+ Thread.currentThread().getName());
            });
        }

        service.shutdown();
    }
  • 我們可以看到結果所以執行緒會正常執行結束後再關閉執行緒池,對於 ShutDown 而言它可以安全的停止一個執行緒池,它有幾個關鍵點
  • ShutDown 會首先將執行緒設定成 SHUTDOWN 狀態,然後中斷所有沒有正在執行的執行緒
  • 正在執行的執行緒和已經在佇列中的執行緒並不會被中斷,說白了就是使用shutDown 方法其實就是要等待所有任務正常全部結束以後才會關閉執行緒池
  • 呼叫 shutdown() 方法後如果還有新的任務被提交,執行緒池則會根據拒絕策略直接拒絕後續新提交的任務。

ShutDownNow

  • 這個方法與上面方法相比較,直觀就是 now ,即立即停止任務,
  • 同樣是上述案列,略作修改如下,
public static void main(String[] args) throws InterruptedException {

        ExecutorService service = new ThreadPoolExecutor(
                10,
                10,
                0L,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(1000));

        for (int i = 0; i < 1000; i++) {
            service.submit(() ->{
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    System.out.println("接受中斷,結束執行緒~~");
                    //這裡響應中斷
                    return;
                }
                System.out.println("args = " + Arrays.deepToString(args)+ Thread.currentThread().getName());
            });
        }

      final List<Runnable> runnables = service.shutdownNow();
       System.out.println(runnables);
    }
  • 執行上述程式碼我們發現,當執行shutDownNow 方法後,會像全部正在執行的佇列通知中斷,正在執行的執行緒接收到中斷訊號後選擇處理,而在佇列中的全部取消執行轉移到一個list佇列中返回,如上述 List<Runnable> runnables ,這裡記錄了所有終止的執行緒

awaitTermination

  • 這個方法並不是用來關閉執行緒池的,首先我們看一下這個方法的定義:

boolean awaitTermination_(long timeout, TimeUnit unit)_

  • 可以看到這個方法有兩個引數,timeout 表示等待的時間,unit 時間單位
  • 這個方法的作用是,呼叫後等待timeout時間後,反饋執行緒池的狀態,
  • 等待期間(包括進入等待狀態之前)執行緒池已關閉並且所有已提交的任務(包括正在執行的和佇列中等待的)都執行完畢,相當於執行緒池已經“終結”了,方法便會返回 true
  • 等待超時時間到後,第一種執行緒池“終結”的情況始終未發生,方法返回 false
  • 等待期間執行緒被中斷,方法會丟擲 InterruptedException 異常。
  • 上面程式碼可以修改來測試,這裡不再貼上程式碼

isShutDown

  • isShutDown 方法正如名字,判斷執行緒池是否停止,返回的是 Boolean 型別,如果已經開始停止執行緒池則返回 true 否則放回false
  • 當呼叫了shutDownshutDownNow 時之後,會返回 true 不過需要注意,這時候只是代表執行緒池關閉流程的開始,並不是說執行緒池已經停止了

isTerminated

  • 這個方法與上面的方法的區別就是這是正真檢測執行緒池是否真的終結了
  • 這不僅代表執行緒池已關閉,同時代表執行緒池中的所有任務都已經都執行完畢了,因為在呼叫 shutdown 方法之後,執行緒池會繼續執行裡面未完成的任務,包括正在執行的任務和在任務佇列中等待的任務。
  • 如果呼叫了 shutdown 方法,但是有一個執行緒依然在執行任務,那麼此時呼叫 isShutdown 方法返回的是 true,而呼叫 isTerminated方法返回的便是 false,因為執行緒池中還有任務正在在被執行,執行緒池並沒有真正“終結”。
  • 直到所有任務都執行完畢了,呼叫 isTerminated() 方法才會返回 true,這表示執行緒池已關閉並且執行緒池內部是空的,所有剩餘的任務都執行完畢了。


本文由AnonyStar 釋出,可轉載但需宣告原文出處。
歡迎關注微信公賬號 :雲棲簡碼 獲取更多優質文章
更多文章關注筆者部落格 :雲棲簡碼 i-code.online

相關文章