目錄
前言
原因分析
1. newFixedThreadPool 和 newSingleThreadExecutor
示例:
2. newCachedThreadPool
示例:
建議的替代方法
示例:
解釋:
總結
前言
????????在Java中,Executors
類提供了幾個工廠方法來創建不同類型的線程池,例如 newFixedThreadPool
、newCachedThreadPool
和 newSingleThreadExecutor
。雖然這些方法方便使用,但它們在某些情況下可能會帶來潛在的問題,因此不建議在生產環境中直接使用 Executors
來創建線程池。下面從原理角度詳細講解原因,并提供相應的示例。
原因分析
1. newFixedThreadPool
和 newSingleThreadExecutor
原理:這兩個方法使用無界隊列 LinkedBlockingQueue
作為任務隊列。
問題:無界隊列意味著隊列可以無限增長。如果任務提交速度快于任務處理速度,隊列中的任務會不斷增加,可能導致內存耗盡(OOM)。
示例:
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < Integer.MAX_VALUE; i++) {executor.submit(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}});
}
在這個示例中,如果任務提交速度非常快,任務會無限制地排隊,最終導致內存耗盡。
2. newCachedThreadPool
原理:該方法使用 SynchronousQueue
作為任務隊列,并且線程池的最大線程數是 Integer.MAX_VALUE
。
問題:SynchronousQueue
不存儲任務,每個插入操作必須等待相應的移除操作。如果任務提交速度快于任務處理速度,會創建大量線程,可能導致系統資源耗盡(如CPU、內存等)。
示例:
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < Integer.MAX_VALUE; i++) {executor.submit(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}});
}
在這個示例中,如果任務提交速度非常快,會創建大量線程,最終導致系統資源耗盡。
建議的替代方法
為了更好地控制線程池的行為,建議使用 ThreadPoolExecutor
構造函數來創建線程池。這樣可以更明確地指定線程池的參數,如核心線程數、最大線程數、任務隊列類型和拒絕策略。
示例:
import java.util.concurrent.*;public class CustomThreadPoolExample {public static void main(String[] args) {// 創建一個具有自定義參數的線程池ThreadPoolExecutor executor = new ThreadPoolExecutor(10, // 核心線程數20, // 最大線程數60L, // 非核心線程的空閑時間TimeUnit.SECONDS, // 空閑時間的單位new ArrayBlockingQueue<>(100), // 有界任務隊列new ThreadPoolExecutor.CallerRunsPolicy() // 拒絕策略);for (int i = 0; i < 200; i++) {executor.submit(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println(Thread.currentThread().getName() + " completed task");});}executor.shutdown();}
}
解釋:
-
核心線程數和最大線程數:
- 核心線程數設置為10,最大線程數設置為20。
- 線程池在初始時創建10個核心線程,任務提交超過10個時,會創建最多10個額外的線程(最大線程數-核心線程數)。
-
非核心線程的空閑時間:
- 非核心線程空閑時間設置為60秒,超過這個時間未處理任務的非核心線程將被終止。
-
有界任務隊列:
- 使用
ArrayBlockingQueue
,容量為100。這限制了任務隊列的大小,避免無限增長導致內存耗盡。
- 使用
-
拒絕策略:
- 使用
CallerRunsPolicy
作為拒絕策略。當任務隊列已滿且所有線程都在運行時,拒絕策略將任務返回給調用者線程執行,避免任務丟失。
- 使用
總結
不建議使用 Executors
創建線程池的原因主要是它隱藏了線程池的具體實現,容易導致以下問題:
- 無界隊列導致內存耗盡:
newFixedThreadPool
和newSingleThreadExecutor
使用無界隊列,可能導致內存耗盡。 - 線程無限增長導致資源耗盡:
newCachedThreadPool
使用SynchronousQueue
和無限制的最大線程數,可能導致系統資源耗盡。
使用 ThreadPoolExecutor
構造函數可以更精細地控制線程池的參數,從而避免這些潛在問題。