若是優雅地關閉ExecutorService提供的java線程池

ExecutorService讓咱們能夠優雅地在程序中使用線程池來建立和管理線程,並且性能佳、開銷小,還能夠有效地控制最大併發線程數,是咱們在java併發編程中會常常使用到的。java

每個線程都會佔用系統資源,所以線程池的關閉與清理一樣重要,本文介紹咱們如何優雅地關閉線程池。web

一. ExecutorService中關閉線程池的方法

1. shutdown()

中止接收新任務,原來的任務繼續執行編程

  1. 中止接收新的submit的任務;
  2. 已經提交的任務(包括正在跑的和隊列中等待的),會繼續執行完成;
  3. 等到第2步完成後,才真正中止;

2. shutdownNow()

中止接收新任務,原來的任務中止執行併發

  1. 跟 shutdown() 同樣,先中止接收新submit的任務;
  2. 忽略隊列裏等待的任務;
  3. 嘗試將正在執行的任務interrupt中斷;
  4. 返回未執行的任務列表;

    說明:它試圖終止線程的方法是經過調用 Thread.interrupt() 方法來實現的,這種方法的做用有限,若是線程中沒有sleep 、wait、Condition、定時鎖等應用, interrupt() 方法是沒法中斷當前的線程的。因此,shutdownNow() 並不表明線程池就必定當即就能退出,它也可能必需要等待全部正在執行的任務都執行完成了才能退出。可是大多數時候是能當即退出的。異步

3. awaitTermination(long timeOut, TimeUnit unit)

當前線程阻塞,timeout 和 TimeUnit 兩個參數,用於設定超時的時間及單位,當前線程阻塞,直到:jvm

  • 等全部已提交的任務(包括正在跑的和隊列中等待的)執行完;
  • 或者 等超時時間到了(timeout 和 TimeUnit設定的時間);
  • 或者 線程被中斷,拋出InterruptedException

而後會監測 ExecutorService 是否已經關閉,返回true(shutdown請求後全部任務執行完畢)或false(已超時)ide

二. 三種方法的區別

1. shutdown() 和 shutdownNow() 的區別

  • shutdown() 只是關閉了提交通道,用submit()是無效的;而內部該怎麼跑仍是怎麼跑,跑完再停。
  • shutdownNow() 能當即中止線程池,正在跑的和正在等待的任務都停下了。

2. shutdown() 和 awaitTermination() 的區別

  • shutdown() 後,不能再提交新的任務進去;可是 awaitTermination() 後,能夠繼續提交。
  • awaitTermination() 是阻塞的,返回結果是線程池是否已中止(true/false);shutdown() 不阻塞。

3. 總結

  1. 優雅的關閉,用 shutdown()
  2. 想立馬關閉,並獲得未執行任務列表,用shutdownNow()
  3. 優雅的關閉,並容許關閉聲明後新任務能提交,用 awaitTermination()
  4. 關閉功能 【從強到弱】 依次是:shuntdownNow() > shutdown() > awaitTermination()

三. RunTime.getRunTime().addShutdownHook()的做用

RunTime.getRunTime().addShutdownHook()的做用就是在JVM銷燬前執行的最後一個線程,經過addShutdownHook添加鉤子,當系統執行完這些鉤子後,jvm纔會關閉,所以咱們能夠在這個線程中把咱們前面使用ExecutorService建立的線程池優雅地關閉掉。
在web3j中異步執行類(Async)中有以下代碼:函數

// 建立線程池
private static final ExecutorService executor = Executors.newCachedThreadPool();

// 添加關閉線程池的鉤子
static {
    Runtime.getRuntime().addShutdownHook(new Thread(() - > shutdown(executor)));
}

// 關閉線程池的鉤子函數
private static void shutdown(ExecutorService executorService) {
    // 第一步:使新任務沒法提交
    executorService.shutdown();
    try {
        // 第二步:等待未完成任務結束
        if(!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
             // 第三步:取消當前執行的任務
            executorService.shutdownNow();
            // 第四步:等待任務取消的響應
            if(!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
                System.err.println("Thread pool did not terminate");
            }
        }
    } catch(InterruptedException ie) {
        // 第五步:出現異常後,從新取消當前執行的任務
        executorService.shutdownNow();
        Thread.currentThread().interrupt(); // 設置本線程中斷狀態
    }
}
相關文章
相關標籤/搜索