自 Java 5 起,java.util.concurrent.ThreadPoolExecutor 成為平台標準線程池實現。它靈活強大,但參數眾多、行為微妙,易導致誤用。本文系統梳理其核心參數、工作流程、擴展點與拒絕策略,助你精準駕馭線程池。
核心構造參數詳解
public ThreadPoolExecutor(
int corePoolSize, // 核心線程數(常駐)
int maximumPoolSize, // 最大線程數
long keepAliveTime, // 非核心線程空閒存活時間
TimeUnit unit, // 時間單位
BlockingQueue workQueue // 任務隊列
)
任務執行決策流程
- 核心階段:運行線程數 < corePoolSize → 直接創建線程執行
- 排隊階段:運行線程數 ≥ corePoolSize → 任務入隊
- 擴容階段:隊列滿 且 運行線程數 < maximumPoolSize → 創建非核心線程執行
- 拒絕階段:隊列滿 且 運行線程數 ≥ maximumPoolSize → 執行拒絕策略
任務完成後,線程從隊列取新任務;非核心線程空閒超過 keepAliveTime 將被回收,池最終收縮至 corePoolSize。
經典場景演示
假設:corePoolSize=3, maximumPoolSize=6, 隊列容量=10,提交 20 個任務:
- 任務 1-3:直接運行(核心線程)
- 任務 4-13:入隊(隊列填滿)
- 任務 14-16:創建非核心線程運行
- 任務 17-20:拋出
RejectedExecutionException
- 實際執行順序:1,2,3,14,15,16,4,5,6,7,8,9,10,11,12,13
關鍵配置陷阱與對策
| 隊列類型 | 行為差異 | 建議場景 |
LinkedBlockingQueue(無界) | 永不滿,maximumPoolSize 失效,keepAliveTime 無意義 | 任務量可控、優先保證不丟失 |
ArrayBlockingQueue(有界) | 觸發擴容/拒絕邏輯,參數全生效 | 需背壓控制、防止 OOM |
shutdown() 非阻塞,僅停止接受新任務;若需等待完成請用 awaitTermination()。未調用 shutdown() 時線程池常駐,適合長期服務。
擴展點:生命周期鉤子
繼承 ThreadPoolExecutor 重寫三大保護方法:
beforeExecute(Thread t, Runnable r):任務啟動前,可做監控、日誌、資源準備
afterExecute(Runnable r, Throwable t):任務結束後(含異常),可做清理、指標上報
terminated():線程池完全關閉後,可做資源釋放
拒絕策略:四大內建與自定義
| 策略 | 行為 | 適用場景 |
AbortPolicy(預設) | 拋出異常 | 關鍵業務,必須感知過載 |
DiscardPolicy | 靜默丟棄 | 非關鍵、可容忍丟失(如日誌) |
CallerRunsPolicy | 調用者線程同步執行 | 自然背壓,平滑限流 |
DiscardOldestPolicy | 丟棄隊頭最老任務,重試入隊 | 優先處理新任務 |
自定義策略只需實現 RejectedExecutionHandler 接口:
executor.setRejectedExecutionHandler((r, e) ->
log.warn("任務 {} 被拒絕,當前隊列 {}", r.hashCode(), e.getQueue().size()));
工廠方法快速選型
Executors.newFixedThreadPool(n):固定大小,對應 core=max=n, 無界隊列
Executors.newCachedThreadPool():0 核心、無界最大、60s 回收,適合大量短任務
Executors.newSingleThreadExecutor():單線程串行,保證順序
Executors.newScheduledThreadPool(n):定時/周期任務,優於 Timer
總結
掌握「核心→隊列→擴容→拒絕」四階段決策,根據業務選擇有界/無界隊列,善用生命周期鉤子與拒絕策略,方能讓 ThreadPoolExecutor 成為高併發系統的穩定基石。
- 上一篇文章: Swing 架構核心:Model 接口體系與數據驅動開發模式
- 下一篇文章: 擴展 RandomAccessFile 實現緩衝 I/O:性能提升 200 倍的實戰優化之路
Copyright ©
程式碼之家 All Rights Reserved