👉 這是一個或許對你有用的社群
《專案實戰(影片)》:從書中學,往事上“練” 《網際網路高頻面試題》:面朝簡歷學習,春暖花開 《架構 x 系統設計》:摧枯拉朽,掌控面試高頻場景題 《精進 Java 學習指南》:系統學習,網際網路主流技術棧 《必讀 Java 原始碼專欄》:知其然,知其所以然

👉這是一個或許對你有用的開源專案國產 Star 破 10w+ 的開源專案,前端包括管理後臺 + 微信小程式,後端支援單體和微服務架構。功能涵蓋 RBAC 許可權、SaaS 多租戶、資料許可權、商城、支付、工作流、大屏報表、微信公眾號、ERP、CRM、AI 大模型等等功能:
Boot 多模組架構:https://gitee.com/zhijiantianya/ruoyi-vue-pro Cloud 微服務架構:https://gitee.com/zhijiantianya/yudao-cloud 影片教程:https://doc.iocoder.cn 【國內首批】支援 JDK 17/21 + SpringBoot 3.3、JDK 8/11 + Spring Boot 2.7 雙版本
-
前言 -
1. 直接使用 Executors 建立執行緒池 -
2. 錯誤配置執行緒數 -
3. 忽略任務佇列的選擇 -
4. 忘記關閉執行緒池 -
5. 忽略拒絕策略 -
6. 任務中未處理異常 -
7. 阻塞任務佔用執行緒池 -
8. 濫用執行緒池 -
9. 未監控執行緒池狀態 -
10. 動態調整執行緒池引數 -
總結

前言
執行緒池是 Java 中處理多執行緒的強大工具,但它不僅僅是“直接用就完事”的工具。
很多小夥伴在用執行緒池時,因為配置不當或忽略細節,踩過許多坑。
今天跟大家一起聊聊執行緒池中容易踩的 10 個坑,以及如何避免這些坑,希望對你會有所幫助。
基於 Spring Boot + MyBatis Plus + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
專案地址:https://github.com/YunaiV/ruoyi-vue-pro 影片教程:https://doc.iocoder.cn/video/
1. 直接使用 Executors 建立執行緒池
許多初學者在建立執行緒池時,直接使用
Executors
提供的快捷方法:
ExecutorService executor = Executors.newFixedThreadPool(
10
);
問題在哪?
-
無界佇列 : newFixedThreadPool
使用的佇列是LinkedBlockingQueue
,它是無界佇列,任務堆積可能會導致記憶體溢位。 -
執行緒無限增長 : newCachedThreadPool
會無限建立執行緒,在任務量激增時可能耗盡系統資源。
示例:記憶體溢位的風險
ExecutorService executor = Executors.newFixedThreadPool(
2
);
for
(
int
i =
0
; i <
1000000
; i++) {
executor.submit(() -> {
try
{
Thread.sleep(
1000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
});
}
任務數遠大於執行緒數,導致任務無限堆積在佇列中,最終可能導致
OutOfMemoryError
。解決辦法
使用
ThreadPoolExecutor
,並明確指定引數:
ThreadPoolExecutor executor =
new
ThreadPoolExecutor(
2
,
4
,
60L
,
TimeUnit.SECONDS,
new
ArrayBlockingQueue<>(
100
),
// 有界佇列
new
ThreadPoolExecutor.AbortPolicy()
// 拒絕策略
);
基於 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
專案地址:https://github.com/YunaiV/yudao-cloud 影片教程:https://doc.iocoder.cn/video/
2. 錯誤配置執行緒數
很多人隨意配置執行緒池引數,比如核心執行緒數 10,最大執行緒數 100,看起來沒問題,但這可能導致效能問題或資源浪費。
示例:錯誤配置導致的執行緒過載
ThreadPoolExecutor executor =
new
ThreadPoolExecutor(
10
,
// 核心執行緒數
100
,
// 最大執行緒數
60L
,
TimeUnit.SECONDS,
new
ArrayBlockingQueue<>(
10
)
);
for
(
int
i =
0
; i <
1000
; i++) {
executor.submit(() -> {
try
{
Thread.sleep(
5000
);
// 模擬耗時任務
}
catch
(InterruptedException e) {
e.printStackTrace();
}
});
}
這種配置在任務激增時,會建立大量執行緒,系統資源被耗盡。
正確配置方式
根據任務型別選擇合理的執行緒數:
-
CPU 密集型 :執行緒數建議設定為 CPU 核心數 + 1
。 -
IO 密集型 :執行緒數建議設定為 2 * CPU 核心數
。
示例:
int
cpuCores = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor executor =
new
ThreadPoolExecutor(
cpuCores +
1
,
cpuCores +
1
,
60L
,
TimeUnit.SECONDS,
new
ArrayBlockingQueue<>(
50
)
);
3. 忽略任務佇列的選擇
任務佇列直接影響執行緒池的行為。如果選錯佇列型別,會帶來很多隱患。
常見佇列的坑
-
無界佇列 :任務無限堆積。 -
有界佇列 :佇列滿了會觸發拒絕策略。 -
優先順序佇列 :容易導致高優先順序任務頻繁搶佔低優先順序任務。
示例:任務堆積導致問題
ThreadPoolExecutor executor =
new
ThreadPoolExecutor(
2
,
4
,
60L
,
TimeUnit.SECONDS,
new
LinkedBlockingQueue<>()
);
for
(
int
i =
0
; i <
100000
; i++) {
executor.submit(() -> System.out.println(Thread.currentThread().getName()));
}
改進方法 :用有界佇列,避免任務無限堆積。
new
ArrayBlockingQueue<>(
100
);
4. 忘記關閉執行緒池
有些小夥伴用完執行緒池後,忘記呼叫
shutdown()
,導致程式無法正常退出。示例:執行緒池未關閉
ExecutorService executor = Executors.newFixedThreadPool(
5
);
executor.submit(() -> System.out.println(
"任務執行中..."
));
// 執行緒池未關閉,程式一直執行
正確關閉方式
executor.shutdown();
try
{
if
(!executor.awaitTermination(
60
, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
}
catch
(InterruptedException e) {
executor.shutdownNow();
}
5. 忽略拒絕策略
當任務佇列滿時,執行緒池會觸發拒絕策略,很多人不知道預設策略(
AbortPolicy
)會直接拋異常。示例:任務被拒絕
ThreadPoolExecutor executor =
new
ThreadPoolExecutor(
1
,
1
,
60L
,
TimeUnit.SECONDS,
new
ArrayBlockingQueue<>(
2
),
new
ThreadPoolExecutor.AbortPolicy()
// 預設策略
);
for
(
int
i =
0
; i <
10
; i++) {
executor.submit(() -> System.out.println(
"任務"
));
}
執行到第四個任務時會丟擲
RejectedExecutionException
。改進:選擇合適的策略
-
CallerRunsPolicy
:提交任務的執行緒自己執行。 -
DiscardPolicy
:直接丟棄新任務。 -
DiscardOldestPolicy
:丟棄最老的任務。
6. 任務中未處理異常
執行緒池中的任務丟擲異常時,執行緒池不會直接丟擲,導致很多問題被忽略。
示例:異常被忽略
executor.submit(() -> {
thrownew
RuntimeException(
"任務異常"
);
});
解決方法
-
捕獲任務內部異常:
executor.submit(() -> {
try
{
thrownew
RuntimeException(
"任務異常"
);
}
catch
(Exception e) {
System.err.println(
"捕獲異常:"
+ e.getMessage());
}
});
-
自定義執行緒工廠:
ThreadFactory factory = r -> {
Thread t =
new
Thread(r);
t.setUncaughtExceptionHandler((thread, e) -> {
System.err.println(
"執行緒異常:"
+ e.getMessage());
});
return
t;
};
7. 阻塞任務佔用執行緒池
如果執行緒池中的任務是阻塞的(如檔案讀寫、網路請求),核心執行緒會被佔滿,影響效能。
示例:阻塞任務拖垮執行緒池
executor.submit(() -> {
Thread.sleep(
10000
);
// 模擬阻塞任務
});
改進方法
-
減少任務的阻塞時間。 -
增加核心執行緒數。 -
使用非同步非阻塞方式(如 NIO)。
8. 濫用執行緒池
執行緒池不是萬能的,某些場景直接使用
new Thread()
更簡單。示例:過度使用執行緒池
一個簡單的短期任務:
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> System.out.println(
"執行任務"
));
executor.shutdown();
這種情況下,用執行緒池反而複雜。
改進方式
new
Thread(() -> System.out.println(
"執行任務"
)).start();
9. 未監控執行緒池狀態
很多人用執行緒池後,不監控其狀態,導致任務堆積、執行緒耗盡的問題被忽略。
示例:監控執行緒池狀態
System.out.println(
"核心執行緒數:"
+ executor.getCorePoolSize());
System.out.println(
"佇列大小:"
+ executor.getQueue().size());
System.out.println(
"已完成任務數:"
+ executor.getCompletedTaskCount());
結合監控工具(如 JMX、Prometheus),實現即時監控。
10. 動態調整執行緒池引數
有些人線上程池設計時忽略了引數調整的必要性,導致後期效能最佳化困難。
示例:動態調整核心執行緒數
executor.setCorePoolSize(
20
);
executor.setMaximumPoolSize(
50
);
即時調整執行緒池引數,能適應業務的動態變化。
總結
執行緒池是強大的工具,但如果我們日常工作中用得不好也非常容易踩坑。
這篇文章透過實際程式碼示例,我們可以清楚看到執行緒池的問題所在及改進方法。
希望這些內容能幫你避免踩坑,寫出高質量的執行緒池程式碼!
執行緒池用得好,效率槓槓的;用得不好,程式天天崩!
歡迎加入我的知識星球,全面提升技術能力。
👉 加入方式,“長按”或“掃描”下方二維碼噢:

星球的內容包括:專案實戰、面試招聘、原始碼解析、學習路線。





文章有幫助的話,在看,轉發吧。
謝謝支援喲 (*^__^*)