在Java中,Executors 類提供了多種靜態工廠方法來建立不同型別的執行緒池。在學習執行緒池的過程中,一定避不開Executors類,掌握這個類的使用、原理、使用場景,對於實際專案開發時,運用自如,以下是一些常用的方法,V哥來一一細說:
-
newCachedThreadPool()
: 建立一個可快取的執行緒池,如果執行緒池中的執行緒超過60秒沒有被使用,它們將被終止並從快取中移除。 -
newFixedThreadPool(int nThreads)
: 建立一個固定大小的執行緒池,其中 nThreads 指定了執行緒池中執行緒的數量。 -
newSingleThreadExecutor()
: 建立一個單執行緒的執行器,它建立單個工作執行緒來執行任務。 -
newScheduledThreadPool(int corePoolSize)
: 建立一個固定大小的執行緒池,它可以根據需要建立新執行緒,但會按照固定延遲執行具有給定初始延遲的任務。 -
newWorkStealingPool(int parallelism)
: 建立一個工作竊取執行緒池,它使用多個佇列,每個執行緒都從自己的佇列中竊取任務。 -
newSingleThreadScheduledExecutor()
: 建立一個單執行緒的排程執行器,它可以根據需要建立新執行緒來執行任務。 -
privilegedThreadFactory()
: 建立一個執行緒工廠,用於建立具有特權訪問的執行緒。 -
defaultThreadFactory()
: 建立一個預設的執行緒工廠,用於建立具有非特權訪問的執行緒。 -
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哥技術創作路上的助推器,不喜勿噴,感謝。