動態

詳情 返回 返回

Java Executors類的9種創建線程池的方法及應用場景分析 - 動態 詳情

在Java中,Executors 類提供了多種靜態工廠方法來創建不同類型的線程池。在學習線程池的過程中,一定避不開Executors類,掌握這個類的使用、原理、使用場景,對於實際項目開發時,運用自如,以下是一些常用的方法,V哥來一一細説:

  1. newCachedThreadPool(): 創建一個可緩存的線程池,如果線程池中的線程超過60秒沒有被使用,它們將被終止並從緩存中移除。
  2. newFixedThreadPool(int nThreads): 創建一個固定大小的線程池,其中 nThreads 指定了線程池中線程的數量。
  3. newSingleThreadExecutor(): 創建一個單線程的執行器,它創建單個工作線程來執行任務。
  4. newScheduledThreadPool(int corePoolSize): 創建一個固定大小的線程池,它可以根據需要創建新線程,但會按照固定延遲執行具有給定初始延遲的任務。
  5. newWorkStealingPool(int parallelism): 創建一個工作竊取線程池,它使用多個隊列,每個線程都從自己的隊列中竊取任務。
  6. newSingleThreadScheduledExecutor(): 創建一個單線程的調度執行器,它可以根據需要創建新線程來執行任務。
  7. privilegedThreadFactory(): 創建一個線程工廠,用於創建具有特權訪問的線程。
  8. defaultThreadFactory(): 創建一個默認的線程工廠,用於創建具有非特權訪問的線程。
  9. unconfigurableExecutorService(ExecutorService executor): 將給定的 ExecutorService 轉換為不可配置的版本,這樣調用者就不能修改它的配置。
這些方法提供了靈活的方式來創建和管理線程池,以滿足不同的併發需求,下面 V 哥來一一介紹一下9個方法的實現以及使用場景。

1. newCachedThreadPool()

newCachedThreadPool 方法是 Java java.util.concurrent 包中的 Executors 類的一個靜態工廠方法。這個方法用於創建一個可緩存的線程池,它能夠根據需要創建新線程,並且當線程空閒超過一定時間後,線程會被終止並從線程池中移除。

下面是 newCachedThreadPool 方法的大致實現原理和源代碼分析:

實現原理

  • 線程創建: 當提交任務到線程池時,如果線程池中的線程數少於核心線程數,會創建新的線程來執行任務。
  • 線程複用: 如果線程池中的線程數已經達到核心線程數,新提交的任務會被放入任務隊列中等待執行。
  • 線程回收: 如果線程池中的線程在一定時間內(默認是60秒)沒有任務執行,它們會被終止,從而減少資源消耗。

源代碼分析

在 Java 的 java.util.concurrent 包中,Executors 類並沒有直接提供 newCachedThreadPool 的實現,而是通過調用 ThreadPoolExecutor 類的構造函數來實現的。以下是 ThreadPoolExecutor 構造函數的調用示例:

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                   60L, TimeUnit.SECONDS,
                                   new SynchronousQueue<Runnable>());
}

參數解釋:

  • corePoolSize: 核心線程數,這裏設置為0,表示線程池不會保留任何核心線程。
  • maximumPoolSize: 最大線程數,這裏設置為 Integer.MAX_VALUE,表示理論上可以創建無限多的線程。
  • keepAliveTime: 當線程數大於核心線程數時,多餘的空閒線程能等待新任務的最長時間,這裏設置為60秒。
  • unit: keepAliveTime 參數的時間單位,這裏是秒。
  • workQueue: 一個任務隊列,這裏使用的是 SynchronousQueue,它是一個不存儲元素的阻塞隊列,每個插入操作必須等待一個相應的移除操作。

實現過程

  • 初始化: 當調用 newCachedThreadPool 時,會創建一個 ThreadPoolExecutor 實例。
  • 任務提交: 當任務提交給線程池時,線程池會檢查是否有空閒線程可以立即執行任務。
  • 線程創建: 如果沒有空閒線程,並且當前線程數小於 maximumPoolSize,則創建新線程執行任務。
  • 任務隊列: 如果當前線程數已經達到 maximumPoolSize,則將任務放入 SynchronousQueue 中等待。
  • 線程複用: 當一個線程執行完任務後,它不會立即終止,而是嘗試從 SynchronousQueue 中獲取新任務。
  • 線程回收: 如果線程在 keepAliveTime 時間內沒有獲取到新任務,它將被終止。

這種設計使得 newCachedThreadPool 非常適合處理大量短生命週期的任務,因為它可以動態地調整線程數量以適應任務負載的變化。然而,由於它可以創建無限多的線程,如果沒有適當的任務隊列來控制任務的數量,可能會導致資源耗盡。因此,在使用 newCachedThreadPool 時,需要謹慎考慮任務的特性和系統的資源限制。

使用場景:

適用於執行大量短期異步任務,尤其是任務執行時間不確定的情況。例如,Web服務器處理大量併發請求,或者異步日誌記錄。

2. newFixedThreadPool(int nThreads)

newFixedThreadPool(int nThreads) 是 Java 中 java.util.concurrent 包的 Executors 類的一個靜態工廠方法。這個方法用於創建一個固定大小的線程池,它能夠確保線程池中始終有固定數量的線程在工作。

以下是 newFixedThreadPool 方法的實現原理、源代碼分析以及實現過程:

實現原理

  • 固定線程數: 線程池中的線程數量始終保持為 nThreads。
  • 任務隊列: 提交的任務首先由核心線程執行,如果核心線程都在忙碌狀態,新任務將被放入一個阻塞隊列中等待執行。
  • 線程複用: 線程池中的線程會重複利用,執行完一個任務後,會立即嘗試從隊列中獲取下一個任務執行。

源代碼分析

newFixedThreadPool 方法是通過調用 ThreadPoolExecutor 類的構造函數來實現的。以下是 ThreadPoolExecutor 構造函數的調用示例:

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(
        nThreads, // 核心線程數
        nThreads, // 最大線程數
        0L,      // 線程空閒時間,這裏設置為0,表示線程不會空閒
        TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>() // 使用阻塞隊列來存儲任務
    );
}

參數解釋:

  • corePoolSize: 核心線程數,這裏設置為 nThreads,表示線程池中始終有 nThreads 個線程。
  • maximumPoolSize: 最大線程數,這裏也設置為 nThreads,表示線程池的線程數量不會超過 nThreads。
  • keepAliveTime: 當線程數大於核心線程數時,多餘的空閒線程能等待新任務的最長時間,這裏設置為0,表示如果線程池中的線程數超過核心線程數,這些線程將立即終止。
  • unit: keepAliveTime 參數的時間單位,這裏是毫秒。
  • workQueue: 一個任務隊列,這裏使用的是 LinkedBlockingQueue,它是一個基於鏈表的阻塞隊列,可以存儲任意數量的任務。

實現過程

  • 初始化: 當調用 newFixedThreadPool 時,會創建一個 ThreadPoolExecutor 實例。
  • 任務提交: 當任務提交給線程池時,線程池會檢查是否有空閒的核心線程可以立即執行任務。
  • 任務隊列: 如果所有核心線程都在忙碌狀態,新提交的任務將被放入 LinkedBlockingQueue 中等待。
  • 線程複用: 核心線程執行完一個任務後,會嘗試從 LinkedBlockingQueue 中獲取新任務繼續執行。
  • 線程數量控制: 由於 keepAliveTime 設置為0,當線程池中的線程數超過核心線程數時,這些線程會立即終止,從而保證線程池中的線程數量不會超過 nThreads。

這種設計使得 newFixedThreadPool 非常適合處理大量且持續的任務,因為它可以保證任務以固定的線程數量並行執行,同時避免了線程數量的無限制增長。然而,由於線程池的大小是固定的,如果任務提交的速率超過了線程池的處理能力,可能會導致任務在隊列中等待較長時間。因此,在使用 newFixedThreadPool 時,需要根據任務的特性和預期的負載來合理設置 nThreads 的值。

使用場景:

適用於執行大量長期運行的任務,其中線程數量需要固定。例如,同時運行多個數據加載或數據處理任務,且希望限制併發數以避免資源過載。

3. newSingleThreadExecutor()

newSingleThreadExecutor 是 Java 中 java.util.concurrent 包的 Executors 類的一個靜態工廠方法,用於創建一個單線程的執行器。這個執行器確保所有任務都按照任務提交的順序,在一個線程中順序執行。

以下是 newSingleThreadExecutor 方法的實現原理、源代碼分析以及實現過程:

實現原理

  • 單線程執行: 線程池中只有一個線程,所有任務都由這個線程順序執行。
  • 任務隊列: 如果這個線程在執行任務時有新任務提交,新任務會被放入一個阻塞隊列中等待執行。
  • 線程複用: 這個線程會重複利用,執行完一個任務後,會立即嘗試從隊列中獲取下一個任務執行。

源代碼分析

newSingleThreadExecutor 方法同樣是通過調用 ThreadPoolExecutor 類的構造函數來實現的。以下是 ThreadPoolExecutor 構造函數的調用示例:

public static ExecutorService newSingleThreadExecutor() {
    return new ThreadPoolExecutor(
        1, // 核心線程數
        1, // 最大線程數
        0L, TimeUnit.MILLISECONDS, // 線程空閒時間,這裏設置為0,表示線程不會空閒
        new LinkedBlockingQueue<Runnable>() // 使用阻塞隊列來存儲任務
    );
}

參數解釋:

  • corePoolSize: 核心線程數,這裏設置為1,表示線程池中始終有一個核心線程。
  • maximumPoolSize: 最大線程數,這裏也設置為1,表示線程池的線程數量不會超過1。
  • keepAliveTime: 線程空閒時間,這裏設置為0,表示如果線程空閒,它將立即終止。
  • unit: keepAliveTime 參數的時間單位,這裏是毫秒。
  • workQueue: 一個任務隊列,這裏使用的是 LinkedBlockingQueue,它是一個無界隊列,可以存儲任意數量的任務。

實現過程

  • 初始化: 當調用 newSingleThreadExecutor 時,會創建一個 ThreadPoolExecutor 實例。
  • 任務提交: 當任務提交給線程池時,如果核心線程空閒,則立即執行任務;如果核心線程忙碌,則將任務放入 LinkedBlockingQueue 中等待。
  • 順序執行: 由於只有一個線程,所有任務都將按照提交的順序被執行。
  • 任務隊列: 如果核心線程在執行任務,新提交的任務將被放入 LinkedBlockingQueue 中排隊等待。
  • 線程複用: 核心線程執行完一個任務後,會嘗試從 LinkedBlockingQueue 中獲取新任務繼續執行。
  • 線程數量控制: 由於 keepAliveTime 設置為0,核心線程在沒有任務執行時會立即終止。但由於 corePoolSize 和 maximumPoolSize 都為1,線程池會立即重新創建一個線程。

這種設計使得 newSingleThreadExecutor 非常適合處理需要保證任務順序的場景,例如,當任務之間有依賴關係或者需要按照特定順序執行時。同時,由於只有一個線程,這也避免了多線程環境下的併發問題。然而,由於只有一個線程執行任務,這也限制了並行處理的能力,如果任務執行時間較長,可能會導致後續任務等待較長時間。因此,在使用 newSingleThreadExecutor 時,需要根據任務的特性和對順序的要求來決定是否適用。

使用場景:

適用於需要保證任務順序執行的場景,例如,順序處理隊列中的消息或事件。也適用於需要單個後台線程持續處理週期性任務的情況。

4. newScheduledThreadPool(int corePoolSize)

newScheduledThreadPool 是 Java 中 java.util.concurrent 包的 Executors 類的一個靜態工廠方法,用於創建一個固定大小的線程池,這個線程池支持定時以及週期性的任務執行。

以下是 newScheduledThreadPool 方法的實現原理、源代碼分析以及實現過程:

實現原理

  • 定時任務: 線程池能夠按照指定的延遲執行任務,或者以固定間隔週期性地執行任務。
  • 固定線程數: 線程池中的線程數量被限制為 corePoolSize 指定的大小。
  • 任務隊列: 任務首先由核心線程執行,如果核心線程都在忙碌狀態,新任務將被放入一個延遲任務隊列中等待執行。

源代碼分析

newScheduledThreadPool 方法是通過調用 ScheduledThreadPoolExecutor 類的構造函數來實現的。以下是 ScheduledThreadPoolExecutor 構造函數的調用示例:

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

這裏的 ScheduledThreadPoolExecutor 是 ThreadPoolExecutor 的一個子類,專門為執行定時任務設計。ScheduledThreadPoolExecutor 構造函數的參數 corePoolSize 定義了線程池中核心線程的數量。

ScheduledThreadPoolExecutor 內部使用了一個 DelayedWorkQueue 作為任務隊列,這個隊列能夠按照任務的預定執行時間對任務進行排序。

實現過程

  • 初始化: 當調用 newScheduledThreadPool 時,會創建一個 ScheduledThreadPoolExecutor 實例。
  • 任務提交: 當任務提交給線程池時,線程池會根據任務的預定執行時間,將任務放入 DelayedWorkQueue 中。
  • 任務調度: 線程池中的線程會從 DelayedWorkQueue 中獲取任務,如果任務的執行時間已經到達,線程將執行該任務。
  • 線程複用: 執行完一個任務的線程會再次嘗試從 DelayedWorkQueue 中獲取下一個任務。
  • 線程數量控制: 如果任務隊列中的任務數量超過了核心線程能夠處理的範圍,ScheduledThreadPoolExecutor 會創建新的線程來幫助處理任務,直到達到 corePoolSize 指定的最大線程數。

特點

  • ScheduledThreadPoolExecutor 允許設置一個線程工廠,用於創建具有特定屬性的線程。
  • 它還允許設置一個 RejectedExecutionHandler,當任務無法被接受時(例如,線程池關閉或任務隊列已滿),這個處理器會被調用。
  • 與 ThreadPoolExecutor 不同,ScheduledThreadPoolExecutor 的 shutdown 和 shutdownNow 方法不會等待延遲任務執行完成。

使用 newScheduledThreadPool 創建的線程池非常適合需要執行定時任務的場景,例如,定期執行的後台任務、定時檢查等。然而,由於它是基於固定大小的線程池,所以在高負載情況下,任務可能會排隊等待執行,這需要在設計時考慮適當的 corePoolSize 以滿足性能要求。

使用場景:

適用於需要定期執行任務或在將來某個時間點執行任務的場景。例如,定時備份數據、定時發送提醒等。

5. newWorkStealingPool(int parallelism)

newWorkStealingPool 是 Java 8 中新增的 java.util.concurrent 包的 Executors 類的一個靜態工廠方法。這個方法用於創建一個工作竊取(Work-Stealing)線程池,它能夠提高並行任務的執行效率,特別是在多處理器系統上。

實現原理

  • 工作竊取: 在工作竊取線程池中,每個線程都有自己的任務隊列。當一個線程完成自己的任務後,它會嘗試從其他線程的任務隊列中“竊取”任務來執行。
  • 並行級別: 線程池的大小由 parallelism 參數決定,這個參數通常等於主機上的處理器核心數。
  • 動態調整: 工作竊取線程池可以動態地添加或移除線程,以適應任務的負載和線程的利用率。

源代碼分析

newWorkStealingPool 方法是通過調用 ForkJoinPool 類的靜態工廠方法 commonPoolFor 來實現的。以下是 ForkJoinPool 構造函數的調用示例:

public static ExecutorService newWorkStealingPool(int parallelism) {
    return new ForkJoinPool(
        parallelism,
        ForkJoinPool.defaultForkJoinWorkerThreadFactory,
        null, // 沒有未處理的異常處理器
        false // 不是一個異步任務
    );
}

參數解釋:

  • parallelism: 線程池的並行級別,即線程池中的線程數量。
  • ForkJoinPool.defaultForkJoinWorkerThreadFactory: 默認的線程工廠,用於創建線程。
  • null: 未處理的異常處理器,這裏沒有指定,因此如果任務拋出未捕獲的異常,它將被傳播到 ForkJoinTask 的調用者。
  • false: 表示這不是一個異步任務。

ForkJoinPool 內部使用了 ForkJoinWorkerThread 來執行任務,並且每個線程都有一個 ForkJoinQueue 來存儲任務。

實現過程

  • 初始化: 當調用 newWorkStealingPool 時,會創建一個 ForkJoinPool 實例。
  • 任務提交: 當任務提交給線程池時,它們會被放入調用線程的本地隊列中。
  • 任務執行: 每個線程首先嚐試執行其本地隊列中的任務。
  • 工作竊取: 如果本地隊列為空,線程會嘗試從其他線程的隊列中竊取任務來執行。
  • 動態調整: 線程池可以根據需要動態地添加或移除線程。

特點

  • 工作竊取線程池特別適合於工作量不均勻分佈的任務,因為它可以減少空閒時間並提高資源利用率。
  • 它也適用於可分解為多個子任務的並行計算任務,因為可以將任務分解後,再將子任務提交給線程池。
  • 由於每個線程都有自己的隊列,因此減少了鎖的爭用,提高了併發性能。

使用 newWorkStealingPool 創建的線程池非常適合於需要高併發和高吞吐量的場景,尤其是在多處理器系統上。然而,由於工作竊取機制,它可能不適用於任務執行時間非常短或者任務數量非常少的場景,因為竊取任務本身可能會引入額外的開銷。

使用場景:

適用於工作量不均勻或可分解為多個小任務的並行計算任務。例如,圖像處理、數據分析等,可以在多核處理器上有效利用所有核心。

6. newSingleThreadScheduledExecutor()

newSingleThreadScheduledExecutor 是 Java 中 java.util.concurrent 包的 Executors 類的一個靜態工廠方法。這個方法用於創建一個單線程的調度執行器,它可以安排命令在給定的延遲後運行,或者定期地執行。

以下是 newSingleThreadScheduledExecutor 方法的實現原理、源代碼分析以及實現過程:

實現原理

  • 單線程執行: 執行器確保所有任務都在單個線程中順序執行,這保證了任務的執行順序。
  • 定時任務: 支持延遲執行和週期性執行任務。
  • 任務隊列: 所有任務首先被放入一個任務隊列中,然後由單線程按順序執行。

源代碼分析

newSingleThreadScheduledExecutor 方法是通過調用 ScheduledThreadPoolExecutor 類的構造函數來實現的。以下是 ScheduledThreadPoolExecutor 構造函數的調用示例:

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
    return new ScheduledThreadPoolExecutor(1);
}

這裏,ScheduledThreadPoolExecutor 是 ExecutorService 的一個實現,專門為執行定時任務設計。構造函數只有一個參數,即核心線程數,這裏設置為1,表示這是一個單線程的執行器。

ScheduledThreadPoolExecutor 內部使用了一個 DelayedWorkQueue 作為任務隊列,這個隊列能夠按照任務的預定執行時間對任務進行排序。

實現過程

  • 初始化: 當調用 newSingleThreadScheduledExecutor 時,會創建一個 ScheduledThreadPoolExecutor 實例,其核心線程數為1。
  • 任務提交: 當任務提交給執行器時,任務會被封裝成 ScheduledFutureTask 或者 RunnableScheduledFuture,然後放入 DelayedWorkQueue 中。
  • 任務調度: 單線程會不斷地從 DelayedWorkQueue 中獲取任務,並按照預定的時間執行。如果任務的執行時間已經到達,任務將被執行;如果還沒有到達,線程會等待直到執行時間到來。
  • 順序執行: 由於只有一個線程,所有任務都將按照它們被提交的順序被執行。
  • 週期性任務: 對於需要週期性執行的任務,執行器會在每次任務執行完畢後,重新計算下一次執行的時間,並再次將任務放入隊列。

特點

  • newSingleThreadScheduledExecutor 創建的執行器非常適合需要保證任務順序的場景,例如,需要按照特定順序執行的任務或者具有依賴關係的任務。
  • 它也適合執行定時任務,如定期執行的維護任務或者後台任務。
  • 由於只有一個線程,這也避免了多線程環境下的併發問題,簡化了任務同步和狀態管理。

使用 newSingleThreadScheduledExecutor 創建的執行器可以提供強大的定時任務功能,同時保持任務執行的順序性。然而,由於只有一個線程執行任務,這也限制了並行處理的能力,如果任務執行時間較長,可能會導致後續任務等待較長時間。因此,在使用 newSingleThreadScheduledExecutor 時,需要根據任務的特性和對順序的要求來決定是否適用。

使用場景:

適用於需要單個後台線程按計劃執行任務的場景。例如,定時檢查系統狀態、定時執行維護任務等。

7. privilegedThreadFactory()

privilegedThreadFactory 是 Java 中 java.util.concurrent 包的 Executors 類的一個靜態工廠方法,用於創建一個線程工廠,該工廠能夠產生具有特權訪問的線程。這意味着這些線程可以加載系統屬性和庫,並且可以訪問文件系統。

以下是 privilegedThreadFactory 方法的實現原理、源代碼分析以及實現過程:

實現原理

  • 特權訪問: 創建的線程將具有訪問系統資源的權限,例如,加載系統屬性和庫。
  • 線程創建: 線程工廠將創建新的線程實例,這些線程實例將繼承創建它們的線程的上下文。

源代碼分析

在 Java 的標準庫中,privilegedThreadFactory 方法的實現細節並未公開,因為它是一個私有方法。然而,我們可以分析其大致工作原理。privilegedThreadFactory 方法的調用示例如下:

public static ThreadFactory privilegedThreadFactory() {
    return new PrivilegedThreadFactory();
}

這裏,PrivilegedThreadFactory 是 Executors 類的一個私有靜態內部類,它實現了 ThreadFactory 接口。ThreadFactory 接口定義了一個 newThread(Runnable r) 方法,用於創建新的線程。

實現過程

  • 初始化: 當調用 privilegedThreadFactory 方法時,會返回一個新的 PrivilegedThreadFactory 實例。
  • 線程創建: 當使用這個工廠創建線程時,它會調用 newThread(Runnable r) 方法。
  • 特權訪問: 在 newThread(Runnable r) 方法的實現中,會使用 AccessController.doPrivileged 方法來確保新創建的線程具有特權訪問。
  • 上下文複製: 通常,新線程會複製創建它的線程的上下文,包括類加載器等。

示例代碼

雖然我們不能查看 privilegedThreadFactory 的具體實現,但是我們可以提供一個示例實現,以展示如何創建具有特權訪問的線程:

public class PrivilegedThreadFactory implements ThreadFactory {
    @Override
    public Thread newThread(Runnable r) {
        return AccessController.doPrivileged(new PrivilegedAction<>() {
            @Override
            public Thread run() {
                return new Thread(r);
            }
        });
    }
}

在這個示例中,PrivilegedAction 是一個實現了 PrivilegedAction<T> 接口的匿名類,其 run 方法創建了一個新的線程。AccessController.doPrivileged 方法用於執行一個特權操作,這裏是為了確保線程創建過程中具有必要的權限。

特點

  • 使用 privilegedThreadFactory 創建的線程可以在需要訪問敏感系統資源的情況下使用。
  • 這種線程工廠通常用於需要執行特權操作的應用程序,例如,訪問系統屬性或者執行文件 I/O 操作。

使用 privilegedThreadFactory 可以確保線程在執行任務時具有適當的安全權限,從而避免安全異常。然而,需要注意的是,過度使用特權訪問可能會帶來安全風險,因此在設計應用程序時應謹慎使用。

使用場景:

適用於需要線程具有更高權限來訪問系統資源的場景。例如,需要訪問系統屬性或執行文件I/O操作的應用程序。

8. defaultThreadFactory()

defaultThreadFactory 是 Java 中 java.util.concurrent 包的 Executors 類的一個靜態工廠方法,用於創建一個默認的線程工廠。這個線程工廠生成的線程沒有特殊的權限,它們是普通的線程,具有標準的訪問權限。

以下是 defaultThreadFactory 方法的實現原理、源代碼分析以及實現過程:

實現原理

  • 標準線程創建: 創建的線程工廠將生成具有默認屬性的線程。
  • 線程名稱: 生成的線程具有默認的線程名稱前綴,通常是 "pool-x-thread-y",其中 x 和 y 是數字。
  • 線程優先級: 線程的優先級設置為 Thread.NORM_PRIORITY,這是 Java 線程的默認優先級。
  • 非守護線程: 創建的線程不是守護線程(daemon threads),它們的存在不會阻止 JVM 退出。

源代碼分析

Java 的 defaultThreadFactory 方法的具體實現細節並未完全公開,因為它是 Executors 類的一個私有靜態方法。但是,我們可以根據 Java 的 ThreadFactory 接口和一些公開的源代碼片段來分析其大致實現。

以下是 defaultThreadFactory 方法的調用示例:

public static ThreadFactory defaultThreadFactory() {
    return new DefaultThreadFactory();
}

這裏,DefaultThreadFactory 是 Executors 類的一個私有靜態內部類,它實現了 ThreadFactory 接口。ThreadFactory 接口定義了一個 newThread(Runnable r) 方法,用於創建新的線程。

實現過程

  • 初始化: 當調用 defaultThreadFactory 方法時,會返回一個新的 DefaultThreadFactory 實例。
  • 線程創建: 使用這個工廠創建線程時,它會調用 newThread(Runnable r) 方法。
  • 設置線程名稱: 在 newThread(Runnable r) 方法的實現中,會創建一個新的 Thread 對象,並設置一個默認的線程名稱。
  • 設置線程組: 新線程會被分配到一個默認的線程組中。
  • 線程優先級和守護狀態: 線程的優先級設置為默認值,且線程不是守護線程。

示例代碼

雖然我們不能查看 defaultThreadFactory 的具體實現,但是我們可以提供一個示例實現,以展示如何創建具有默認屬性的線程:

public class DefaultThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    DefaultThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" +
                     poolNumber.getAndIncrement() +
                     "-thread-";
    }

    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,
                              namePrefix + threadNumber.getAndIncrement(),
                              0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

在這個示例中,DefaultThreadFactory 使用 AtomicInteger 來確保線程池和線程編號的唯一性。創建的線程名稱具有前綴 "pool-x-thread-y",其中 x 和 y 是自增的數字。線程不是守護線程,且優先級設置為 Thread.NORM_PRIORITY。

特點

  • 使用 defaultThreadFactory 創建的線程工廠生成的線程具有標準的 Java 線程屬性。
  • 這種線程工廠通常用於不需要特殊權限的應用程序。
  • 由於線程不是守護線程,它們的存在可以維持 JVM 的運行,直到所有非守護線程執行完畢。

使用 defaultThreadFactory 可以確保線程在執行任務時具有標準的安全和執行屬性,適合大多數常規用途。然而,如果應用程序需要特殊的線程屬性,如守護線程或不同的優先級,可能需要自定義線程工廠。

使用場景:

適用於大多數標準應用程序,需要創建具有默認屬性的線程。這是大多數 ExecutorService 實現的默認選擇。

9. unconfigurableExecutorService(ExecutorService executor)

unconfigurableExecutorService 是 Java 中 java.util.concurrent 包的 Executors 類的一個靜態工廠方法。這個方法用於創建一個不可配置的 ExecutorService 包裝器,這意味着一旦包裝後的 ExecutorService 被創建,就不能更改其配置,比如不能修改其線程池大小或任務隊列等。

以下是 unconfigurableExecutorService 方法的實現原理、源代碼分析以及實現過程:

實現原理

  • 封裝: 將現有的 ExecutorService 封裝在一個不可配置的代理中。
  • 不可修改: 所有修改配置的方法調用,如 shutdown, shutdownNow, setCorePoolSize 等,都將拋出 UnsupportedOperationException。
  • 轉發: 除了配置修改的方法外,其他方法調用將被轉發到原始的 ExecutorService。

源代碼分析

unconfigurableExecutorService 方法的具體實現細節並未完全公開,因為它是 Executors 類的一個私有靜態方法。但是,我們可以根據 Java 的 ExecutorService 接口和代理機制來分析其大致實現。

以下是 unconfigurableExecutorService 方法的調用示例:

public static ExecutorService unconfigurableExecutorService(ExecutorService executor) {
    return new FinalizableDelegatedExecutorService(executor);
}

這裏,FinalizableDelegatedExecutorService 是 Executors 類的一個私有靜態內部類,它實現了 ExecutorService 接口,並代理了對另一個 ExecutorService 的調用。

實現過程

  • 初始化: 當調用 unconfigurableExecutorService 方法時,會返回一個新的 FinalizableDelegatedExecutorService 實例,它將原始的 ExecutorService 作為參數。
  • 方法調用攔截: 對 FinalizableDelegatedExecutorService 的方法調用將首先被攔截。
  • 配置修改攔截: 如果調用的方法是用於修改配置的,比如 shutdown 或 shutdownNow,將拋出 UnsupportedOperationException。
  • 轉發其他調用: 對於其他不涉及配置修改的方法調用,比如 submit, execute, 將被轉發到原始的 ExecutorService。

示例代碼

下面V哥來模擬一個示例實現,以展示如何創建一個不可配置的 ExecutorService 代理:

public class UnconfigurableExecutorService implements ExecutorService {
    private final ExecutorService executor;

    public UnconfigurableExecutorService(ExecutorService executor) {
        this.executor = executor;
    }

    @Override
    public void shutdown() {
        throw new UnsupportedOperationException("Shutdown not allowed");
    }

    @Override
    public List<Runnable> shutdownNow() {
        throw new UnsupportedOperationException("Shutdown not allowed");
    }

    @Override
    public boolean isShutdown() {
        return executor.isShutdown();
    }

    @Override
    public boolean isTerminated() {
        return executor.isTerminated();
    }

    @Override
    public void execute(Runnable command) {
        executor.execute(command);
    }

    // 其他 ExecutorService 方法的實現,遵循相同的模式
}

在這個示例中,UnconfigurableExecutorService 攔截了 shutdown 和 shutdownNow 方法,並拋出了異常。其他方法則直接轉發到原始的 ExecutorService。

特點

  • 使用 unconfigurableExecutorService 創建的 ExecutorService 代理確保了線程池的配置不能被外部修改。
  • 這可以用於防止意外地更改線程池的狀態,提高線程池使用的安全性。
  • 除了配置修改的方法外,其他所有方法都保持了原有 ExecutorService 的行為。

使用 unconfigurableExecutorService 可以為現有的 ExecutorService 提供一個安全層,確保它們的狀態不會被意外地更改。這對於在多線程環境中共享 ExecutorService 時特別有用。

使用場景:

適用於需要確保線程池配置在創建後不被更改的場景。例如,當多個組件共享同一個線程池時,可以防止一個組件意外修改配置。

最後

以上是V哥在授課過程中整理的關於Executors 9種創建線程池的方法及原理分析,分享給大家,希望對正在學習 Java 的你有所幫助,每天分享技術乾貨,歡迎關注威哥愛編程,你的鼓勵是V哥技術創作路上的助推器,不喜勿噴,感謝。

user avatar limingxin 頭像 lyflexi 頭像 ranck 頭像 huobaodejianpan 頭像 tuantuantuanzi 頭像 hai2007 頭像 wangxiao_5cdbd71543154 頭像 obkoro1 頭像
點贊 8 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.