一、寫在前面
參考阿里開發規約,創建線程池一般用ThreadPoolExecutor
在高并發程序中,頻繁創建與銷毀線程是一種極其低效且不可控的行為。為了解決這個問題,Java 提供了線程池(ThreadPoolExecutor
)這一強大的并發框架。它不僅提高了任務處理的效率,也幫助我們更好地控制系統資源,是現代高性能服務架構中的基石。
本文將從線程池的概念、設計目的、核心實現機制、任務調度流程、Worker 管理機制等方面入手
1.1 線程池是什么
線程池是一種線程復用機制,它維護了一組可以重復使用的工作線程,任務到來時無需創建新線程,而是復用已有線程執行任務,任務執行完畢后線程不會被銷毀,而是回到池中待命。
Java 中的核心實現是 java.util.concurrent.ThreadPoolExecutor
。
1.2 線程池解決了什么問題
問題 | 線程池的解決方案 |
---|---|
線程創建銷毀開銷大 | 線程復用,避免頻繁構造/銷毀 |
系統資源不可控 | 線程數量上限可設,避免 OOM |
并發請求過多無法及時處理 | 使用阻塞隊列緩存任務,異步處理 |
調度和控制困難 | 提供任務調度、拒絕策略、監控接口 |
缺乏任務管理能力 | 可統一提交、執行、取消任務 |
二、線程池核心設計與實現
2.1 總體設計架構
Java 線程池主要由以下組件構成:
┌──────────────┐ submit(Runnable)
│ 主線程調用 ├────────────────┐
└──────────────┘ ▼┌──────────────┐│任務提交 execute│└─────┬────────┘▼┌────────────┐ (1)創建核心線程執行任務│ WorkerSet │ ←────────────────────────┐└────┬───────┘ │▼ │┌────────────────┐ ││ BlockingQueue │ ←───────(2)入隊等待執行 │└──────┬─────────┘ │▼ │(3)隊列滿,擴容線程池 (4)滿了,執行拒絕策略▼ ▼┌────────────────┐ ┌────────────────┐│ maximumPoolSize│ │ rejectHandler()│└────────────────┘ └────────────────┘
2.2 生命周期管理
線程池有五種狀態,由高位表示,低位為線程數量(workerCount),統一由 ctl
原子變量管理。
狀態 | 描述 |
---|---|
RUNNING | 接收新任務,處理隊列 |
SHUTDOWN | 拒收新任務,繼續處理隊列 |
STOP | 拒收新任務,中斷執行線程 |
TIDYING | 所有任務執行完,清理中 |
TERMINATED | 清理完成,徹底關閉 |
生命周期狀態在任務提交、線程退出、關閉線程池等場景中都會動態變化。
2.3 任務執行機制
2.3.1 任務調度流程(execute)
executor.execute(task) 時的完整流程:
-
線程池運行狀態檢查,若不是 RUNNING,直接拒絕任務。
-
線程數 < corePoolSize:創建新線程執行任務。
-
線程數 ≥ corePoolSize:嘗試將任務放入隊列。
-
隊列已滿 && 線程數 < maximumPoolSize:創建非核心線程執行任務。
-
隊列滿 && 已達最大線程數:執行拒絕策略(默認拋異常)。
2.3.2 任務緩沖機制
任務隊列用于緩存尚未執行的任務:
隊列類型 | 特性 |
---|---|
ArrayBlockingQueue | 有界 FIFO 隊列,避免內存溢出 |
LinkedBlockingQueue | 默認無界隊列,適合任務速率平穩 |
SynchronousQueue | 不存儲任務,適用于高并發 |
PriorityBlockingQueue | 支持優先級任務 |
任務是否入隊,決定了線程是否擴容。
2.3.3 任務申請(線程取任務)
任務的獲取由 getTask()
方法完成:
-
優先從隊列中 poll 一個任務
-
若獲取失敗,且超出 keepAliveTime,線程會退出
-
如果線程池關閉,線程立即退出
2.3.4 任務拒絕策略
當線程池無法接收任務時,會調用 RejectedExecutionHandler
:
策略 | 描述 |
---|---|
AbortPolicy (默認) | 拋出異常 |
DiscardPolicy | 直接丟棄任務 |
DiscardOldestPolicy | 丟棄隊列中最老的任務 |
CallerRunsPolicy | 調用線程自己執行任務(削峰) |
合理的拒絕策略有助于保護系統穩定性。
2.4 Worker 線程管理機制
2.4.1 Worker 線程
Worker 是線程池中用于執行任務的線程封裝類,它本質是一個實現了 Runnable 接口的任務執行單元,每個 Worker 包含:
-
一個真正的 Thread 對象
-
一個任務任務引用
-
一個 run() 循環體:從隊列中不斷拉取任務執行
2.4.2 Worker 線程的創建(addWorker)
線程池根據當前任務和線程數決定是否調用 addWorker()
增加線程:
-
當線程數 < corePoolSize:創建核心線程
-
當隊列已滿 && 線程數 < maximumPoolSize:創建臨時線程
使用 ThreadFactory
可定制線程名稱、優先級等。
2.4.3 Worker 線程的回收
非核心線程超過 keepAliveTime
會被自動回收:
-
降低資源占用
-
避免長期占用 CPU/內存
-
通過
allowCoreThreadTimeOut(true)
也可使核心線程超時回收
回收邏輯在 getTask()
中實現。
2.4.4 Worker 線程執行任務流程
Worker 啟動后,在 run()
方法中循環調用:
while ((task != null || (task = getTask()) != null)) {beforeExecute()try {task.run();} finally {afterExecute()}
}
執行完任務后嘗試從隊列中取下一個任務,直到線程超時或線程池關閉。
總結
Java 的線程池設計是非常經典的“并發容器”之一,其優雅地處理了:
-
線程的 復用與銷毀
-
任務的 排隊與拒絕
-
系統的 穩定性與擴展性
它不是簡單的任務線程分發器,更是一個具備“智能調度、彈性擴容、精細管控”能力的多線程平臺。
附錄:推薦配置建議
場景 | 核心參數建議 |
---|---|
接口服務 | corePoolSize = CPU 核心數,隊列有限,拒絕策略為 CallerRuns |
IO 密集 | maximumPoolSize 設置略高,使用 SynchronousQueue |
定時任務 | 使用 ScheduledThreadPoolExecutor |
高并發網關 | 使用自定義線程工廠 + 有界隊列 + 指標監控 |