為什麼需要線程池?
- 降低資源消耗:復用線程,避免頻繁創建/銷毀開銷。
- 提高響應速度:任務到達無需等待線程創建即可執行。
- 提高可管理性:統一控制併發數、隊列長度、拒絕策略,防止資源耗盡(OOM)。
七大核心構造參數
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
| 參數 | 含義 | 關鍵行為 |
corePoolSize | 核心線程數 | 線程池創建後初始為 0,任務到達創建線程直到達到核心數;之後任務入隊列 |
maximumPoolSize | 最大線程數 | 隊列滿時創建非核心線程救急;達到上限後觸發拒絕策略 |
keepAliveTime | 非核心線程存活時間 | 空閒超過此時間回收;若 allowCoreThreadTimeOut(true) 核心線程也受控 |
unit | 時間單位 | TimeUnit 枚舉:DAYS/HOURS/MINUTES/SECONDS/MILLISECONDS/MICROSECONDS/NANOSECONDS |
workQueue | 任務隊列 | 核心線程滿後任務緩存區,選型直接決定線程池行為 |
threadFactory | 線程工廠 | 自定義線程名、優先級、守護屬性,便於排查日誌 |
handler | 拒絕策略 | 隊列滿且線程達最大時如何處理新任務 |
四大主流隊列選型對比
| 隊列 | 特點 | 適用場景 | 風險 |
ArrayBlockingQueue | 有界數組、FIFO | 固定緩存、需公平調度 | 需預估容量,滿則拒絕 |
LinkedBlockingQueue | 無界鏈表(默認 Integer.MAX_VALUE)、FIFO | 任務量不可預測、優先保證不丟任務 | 可能撐爆內存,maximumPoolSize 失效 |
SynchronousQueue | 零容量、直接移交、生產者消費者配對 | 高吞吐、任務輕量、不想緩存 | 無緩存,突發流量易創建過多線程 |
PriorityBlockingQueue | 優先級堆、無界 | 任務有優先級(如延遲任務、重要任務優先) | 同無界風險,需配合最大線程數控制 |
四種內置拒絕策略
| 策略 | 行為 | 適用場景 |
AbortPolicy (默認) | 拋 RejectedExecutionException | 關鍵業務,必須感知拒絕 |
CallerRunsPolicy | 調用者線程直接執行任務 | 緩衝峰值、不想丟任務、允許調用者阻塞 |
DiscardPolicy | 默默丟棄 | 非核心日誌、監控數據可丟 |
DiscardOldestPolicy | 丟棄隊列頭部最老任務,重新提交 | 優先處理新任務、舊任務可丟 |
執行流程圖解
提交任務
↓
corePoolSize 未滿? → 是 → 創建核心線程執行 → 結束
↓ 否
workQueue 未滿? → 是 → 入隊列等待 → 核心線程取出執行 → 結束
↓ 否
maximumPoolSize 未滿? → 是 → 創建非核心線程執行 → 結束
↓ 否
執行 RejectedExecutionHandler → 結束
生產級配置模板
@Configuration
public class ThreadPoolConfig {
@Bean("bizThreadPool")
public ThreadPoolExecutor bizThreadPool() {
return new ThreadPoolExecutor(
10, // corePoolSize
20, // maximumPoolSize
60L, // keepAliveTime
TimeUnit.SECONDS, // unit
new ArrayBlockingQueue<>(500), // workQueue:有界,防止 OOM
new CustomThreadFactory("biz-"), // threadFactory:業務標識前綴
new ThreadPoolExecutor.CallerRunsPolicy() // handler:削峰填谷
);
}
// 自定義線程工廠
static class CustomThreadFactory implements ThreadFactory {
private final AtomicInteger seq = new AtomicInteger(0);
private final String prefix;
CustomThreadFactory(String prefix) { this.prefix = prefix; }
@Override public Thread newThread(Runnable r) {
Thread t = new Thread(r, prefix + seq.incrementAndGet());
t.setDaemon(false); // 非守護線程,防止 JVM 過早退出
return t;
}
}
}
動態調優:運行時修改參數
ThreadPoolExecutor executor = (ThreadPoolExecutor) threadPoolBean;
executor.setCorePoolSize(20); // 動態調整核心數
executor.setMaximumPoolSize(50); // 動態調整最大數
executor.setKeepAliveTime(120, TimeUnit.SECONDS);
executor.setThreadFactory(newFactory); // 甚至可換工廠
// 隊列無法動態換,需重建線程池
監控關鍵指標
| 指標 | 獲取方式 | 告警閾值建議 |
| 活躍線程數 | getActiveCount() | > 80% maximumPoolSize |
| 隊列堆積 | getQueue().size() | > 70% 隊列容量 |
| 已完成任務數 | getCompletedTaskCount() | 吞吐量突降 |
| 拒絕次數 | 自定義 handler 計數 | > 0 即告警 |
避坑指南
- 慎用 Executors.xxx:
FixedThreadPool、SingleThreadPool 使用無界隊列,CachedThreadPool 最大線程數 Integer.MAX_VALUE,極易 OOM。
- IO 密集型:核心數 = CPU 核心數 * 2(經驗值),隊列較大。
- CPU 密集型:核心數 = CPU 核心數 + 1,隊列較小。
- 異常處理:
execute() 吞異常,用 submit() + Future.get() 捕獲,或重寫 afterExecute() 記錄異常。
- 優雅關閉:
shutdown() 停止接收新任務,等待隊列清空;shutdownNow() 嘗試中斷執行中任務,返回未執行任務列表。
總結
線程池核心在於三個尺寸(core/max/queue)與一個策略(handler)的協同。生產環境拒絕 Executors 快捷方式,顯式配置有界隊列、自定義線程名、CallerRunsPolicy 削峰,並建立監控告警體系,才能駕馭高併發場景。