首頁 > Java > java教程 > 掌握 ExecutorService 關閉:追蹤線程池終止

掌握 ExecutorService 關閉:追蹤線程池終止

Patricia Arquette
發布: 2025-01-05 01:50:38
原創
889 人瀏覽過

Mastering ExecutorService Shutdown: Tracking ThreadPool Termination

假設您想要執行一些任務。由於透過單一執行緒執行它可能需要相當長的時間才能獲得結果,因此您決定使用可靠的 ExecutorService 透過多個執行緒處理它。

這是一個範例:

public static void main(String[] args) {
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    for (int i = 0; i < 5; i++) {
        int temp = i;
        executorService.submit(() -> {
            task(temp);
        });
    }
    executorService.shutdown();
    System.out.println("ExecutorService is shutdown");
}

private static void task(int temp) {
    try {
        TimeUnit.SECONDS.sleep(1L);
        System.out.println("Task " + temp + " completed");
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}
登入後複製
登入後複製

當然,像往常一樣,如果不使用「睡眠」作為任務執行的原型,任何執行緒範例都是不完整的。

它輸出,

ExecutorService is shutdown
Task 1 completed
Task 2 completed
Task 0 completed
Task 4 completed
Task 3 completed
登入後複製
登入後複製

現在想像一下有一個無窮無盡的任務隊列,而你不知道其中的數量。也許它們是由資料庫中動態添加的條目數量決定的。

例如,一家銀行,它必須全天處理大量交易。交易結束時間為下午5點,超過該時間將不再接受任何額外任務。

但是,您確實知道任務的數量是有限的,並且在某個時間點結束。

如何知道所有任務完成的時間點?

如果您注意到上面的程式碼片段,ExecutorService.shutdown() 會使主執行緒立即退出,但後台執行緒仍然處理已接受的任務直至完成。有什麼方法可以讓您收到完成通知嗎?

我想到了幾個解決方案:

  1. 使用 CountDownLatch 來計算任務 - 但由於您不知道任務的數量,所以使用它是不切實際的。
  2. 使用ExecutorService.awaitTermination()。然而,這裡的時間仍然是不確定的。您可以使用非常自由的 ExecutorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS) 或類似的東西。但這又是一個阻塞呼叫。

有更好的方法來解決這個問題嗎?

Java 確實提供了一種更好且相對未知的方法來解決這個問題。這裡的「技巧」是要知道 Executors.newFixedThreadPool 本質上是一個具有預定義值的 ThreadPoolExecutor。讓我們來看看 Executors.newFixedThreadPool 的實作。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
登入後複製
登入後複製

我真的建議閱讀這裡的 ThreadPoolExecutor 文件。 ExecutorService 是 ThreadPoolExecutor 的便利包裝。

...強烈建議程式設計師使用更方便的Executors工廠方法Executors.newCachedThreadPool()(無界執行緒池,具有自動執行緒回收功能)、Executors.newFixedThreadPool(int)(固定大小執行緒池)和Executors.newSingleThreadExecutoringleThreadExecutoringle ( )(單後台執行緒),為最常見的使用場景預先配置設定。

將幫助我們解決問題的部分是:

鉤子方法

此類提供了受保護的可重寫 beforeExecute(Thread, Runnable) 和 afterExecute(Runnable, Throwable) 方法,這些方法在每個任務執行之前和之後都被呼叫。這些可用於操縱執行環境;例如,重新初始化 ThreadLocals、收集統計資料或新增日誌條目。此外,可以重寫方法終止()以執行執行器完全終止後需要完成的任何特殊處理。

我們可以使用終止方法來通知我們同樣的事情!但我們該如何使用它呢?

public static void main(String[] args) {
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    for (int i = 0; i < 5; i++) {
        int temp = i;
        executorService.submit(() -> {
            task(temp);
        });
    }
    executorService.shutdown();
    System.out.println("ExecutorService is shutdown");
}

private static void task(int temp) {
    try {
        TimeUnit.SECONDS.sleep(1L);
        System.out.println("Task " + temp + " completed");
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}
登入後複製
登入後複製

如果您不喜歡匿名類別(像我一樣),您始終可以自行擴展 ThreadPoolExecutor 來建立自訂類別。

ExecutorService is shutdown
Task 1 completed
Task 2 completed
Task 0 completed
Task 4 completed
Task 3 completed
登入後複製
登入後複製

這是驗證它是否按照我們的預期工作的輸出。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
登入後複製
登入後複製

您還使用了哪些其他相對未知的片段?請在評論中告訴我!

以上是掌握 ExecutorService 關閉:追蹤線程池終止的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板