為什么阿里禁止使用Executors去創(chuàng)建線程池
在阿里巴巴的《Java開發(fā)手冊》中有明確規(guī)定:
【強(qiáng)制】線程池不允許使用
Executors去創(chuàng)建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學(xué)更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險。
阿里禁止使用Executors并不是說這個類的方法本身有致命的Bug,而是因?yàn)樗?strong>隱藏了資源耗盡的風(fēng)險,不符合大廠對于系統(tǒng)穩(wěn)定性和可觀測性的苛刻要求。
下面我們以newFixedThreadPool方法為例詳細(xì)拆解一下它的缺陷以及阿里的考量。
一、Executors.newFixedThreadPool()的底層原理與核心缺陷
首先,我們來看一下這個方法的源碼:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}從源碼可以看到,它創(chuàng)建了一個線程池,其核心參數(shù)是:
- 核心線程數(shù) = 最大線程數(shù):
nThreads - 空閑線程存活時間:0(因?yàn)楹诵木€程數(shù)和最大線程數(shù)相等,這個參數(shù)失效)
- 工作隊列:
new LinkedBlockingQueue<Runnable>()【這是問題的核心】
核心缺陷:無界的工作隊列
LinkedBlockingQueue 在默認(rèn)情況下(無參構(gòu)造)是一個 無界隊列。這意味著它的容量是 Integer.MAX_VALUE,理論上可以無限地向其中添加任務(wù)。
這會導(dǎo)致一個嚴(yán)重的問題:當(dāng)任務(wù)提交的速度持續(xù)超過線程處理的速度時,任務(wù)會在無界隊列中無限堆積。
這個“無限堆積”會引發(fā)一系列連鎖反應(yīng):
1、內(nèi)存耗盡(OOM):
- 每個排隊等待執(zhí)行的任務(wù)都會占用一定的內(nèi)存(例如,任務(wù)對象本身、捕獲的上下文變量等)。
- 如果任務(wù)產(chǎn)生速度極快(如突發(fā)流量、循環(huán)中提交任務(wù)),而處理速度很慢(如依賴的外部服務(wù)響應(yīng)慢),隊列中的任務(wù)會越來越多,最終占滿整個堆內(nèi)存,導(dǎo)致
OutOfMemoryError: Java heap space。這會直接導(dǎo)致整個應(yīng)用崩潰,而不是僅僅線程池不可用。
2、延遲和響應(yīng)緩慢:
- 即使沒有達(dá)到OOM,任務(wù)在隊列中排隊的時間也會非常長。一個任務(wù)可能被提交后很久才得到執(zhí)行,導(dǎo)致系統(tǒng)整體響應(yīng)時間變得不可接受。
3、問題定位困難:
- 由于隊列是無界的,系統(tǒng)在OOM之前可能看起來“正常”運(yùn)行,只是有點(diǎn)慢。當(dāng)OOM發(fā)生時,你只能看到一個內(nèi)存溢出的錯誤,但很難快速定位到是哪個線程池、為什么積累了這么多任務(wù)。缺乏必要的監(jiān)控和預(yù)警。
二、 為什么阿里等大廠明確禁止使用?
禁止的原因正是基于上述缺陷,并上升到工程規(guī)范和架構(gòu)層面:
1、規(guī)避風(fēng)險,保證系統(tǒng)穩(wěn)定性:
- 大廠的應(yīng)用通常是高并發(fā)、海量數(shù)據(jù)的場景,對穩(wěn)定性要求極高。任何可能導(dǎo)致服務(wù)雪崩或OOM的風(fēng)險都必須從源頭規(guī)避。使用有界隊列是構(gòu)建“韌性系統(tǒng)”的基本要求。
2、提倡資源管理的顯式化:
Executors提供的工廠方法像是一個“黑盒”,它隱藏了ThreadPoolExecutor的關(guān)鍵參數(shù)(如隊列類型和大?。Mㄟ^強(qiáng)制使用ThreadPoolExecutor構(gòu)造函數(shù),開發(fā)者必須顯式地設(shè)置核心線程數(shù)、最大線程數(shù)、隊列容量、拒絕策略等。這個過程迫使開發(fā)者去思考線程池的配置是否合理,加深對線程池運(yùn)行機(jī)制的理解。
3、需要合理的拒絕策略:
- 當(dāng)使用有界隊列時,隊列滿了之后,就需要拒絕策略來處理新提交的任務(wù)。
ThreadPoolExecutor提供了幾種內(nèi)置策略,如:AbortPolicy(默認(rèn)):拋出RejectedExecutionException,讓調(diào)用者感知到異常。CallerRunsPolicy:讓提交任務(wù)的線程自己去執(zhí)行它,這相當(dāng)于一種負(fù)反饋,能有效平緩任務(wù)提交速率。DiscardPolicy/DiscardOldestPolicy:丟棄任務(wù)。
- 通過選擇合適的拒絕策略,可以在系統(tǒng)過載時提供一種“優(yōu)雅降級”的方案,而不是像無界隊列那樣默默地吃掉所有內(nèi)存直至崩潰。
三、 正確的使用姿勢
應(yīng)該直接使用 ThreadPoolExecutor 來創(chuàng)建線程池,并為其設(shè)置一個有界的工作隊列。
import java.util.concurrent.*;
public class SafeThreadPoolExample {
public static void main(String[] args) {
// 核心參數(shù)
int corePoolSize = 10;
int maximumPoolSize = 20;
long keepAliveTime = 60L;
int queueCapacity = 1000; // 關(guān)鍵:設(shè)置隊列容量
// 手動創(chuàng)建線程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(queueCapacity), // 有界隊列
new ThreadFactory() { // 可選:自定義線程工廠,便于命名和監(jiān)控
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "MyApp-Thread-" + r.hashCode());
t.setDaemon(false);
return t;
}
},
new ThreadPoolExecutor.CallerRunsPolicy() // 關(guān)鍵:設(shè)置合適的拒絕策略
);
// 使用 executor 提交任務(wù)...
// executor.execute(() -> { ... });
// 優(yōu)雅關(guān)閉
// executor.shutdown();
}
}總結(jié)
| 特性/方式 | Executors.newFixedThreadPool() | 手動 ThreadPoolExecutor |
|---|---|---|
| 隊列類型 | 無界 (LinkedBlockingQueue) | 有界 (可配置,如 new LinkedBlockingQueue<>(capacity)) |
| 資源風(fēng)險 | 高,可能導(dǎo)致內(nèi)存溢出(OOM) | 低,通過容量控制和管理 |
| 拒絕策略 | 初期無效,OOM前相當(dāng)于無拒絕 | 有效,隊列滿后觸發(fā),可預(yù)測系統(tǒng)行為 |
| 可觀測性 | 差,隊列長度無上限,監(jiān)控困難 | 好,隊列有界,便于監(jiān)控隊列堆積情況 |
| 阿里規(guī)范 | 禁止 | 推薦 |
結(jié)論:Executors.newFixedThreadPool() 的主要缺陷在于其無界工作隊列帶來的內(nèi)存溢出風(fēng)險。阿里等大廠禁止使用主要是為了規(guī)避上述風(fēng)險,同時培養(yǎng)開發(fā)者的資源邊界意識和風(fēng)險意識,強(qiáng)制通過 ThreadPoolExecutor 的顯式配置來構(gòu)建更健壯、更可控、更易于監(jiān)控的線程池,從而保障大規(guī)模分布式系統(tǒng)的穩(wěn)定性。
到此這篇關(guān)于為什么阿里禁止使用Executors去創(chuàng)建線程池的文章就介紹到這了,更多相關(guān)阿里禁止使用Executors創(chuàng)建線程池內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java 中序列化NotSerializableException問題解決辦法
這篇文章主要介紹了java 中序列化NotSerializableException問題解決辦法的相關(guān)資料,這里對序列化問題進(jìn)行描述說明,并提供解決辦法,希望能幫助到大家,需要的朋友可以參考下2017-08-08
Spring監(jiān)聽器及定時任務(wù)實(shí)現(xiàn)方法詳解
這篇文章主要介紹了Spring監(jiān)聽器及定時任務(wù)實(shí)現(xiàn)方法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-07-07
Java?Swing實(shí)現(xiàn)自定義窗口CusFrame
文章介紹了自定義Swing窗口CusFrame的核心功能,包括移除系統(tǒng)默認(rèn)標(biāo)題欄、實(shí)現(xiàn)圓角矩形形狀、支持拖拽移動和調(diào)整大小、多屏幕適配以及窗口狀態(tài)管理等功能,最后文章提供了使用示例和注意事項,有需要的小伙伴可以參考下2026-05-05
從application.properties配置文件獲取的漢字亂碼的解決方法
平時從配置文件各種讀取配置參數(shù)都正常,但是有時候放了個中文就亂碼,你肯定試過網(wǎng)上好多方法,都沒解決,那么來看下面,恭喜你終于找這里了,本文給大家介紹了從application.properties配置文件獲取的漢字亂碼的解決方法,需要的朋友可以參考下2024-03-03
MyBatis?原生二級緩存"難以修復(fù)"的原因解析及解決方案
文章主要討論了MyBatis原生二級緩存存在的問題,包括結(jié)構(gòu)性缺陷、難以修復(fù)等,社區(qū)提供了多種增強(qiáng)插件方案,但這些方案也有各自的優(yōu)缺點(diǎn),本文結(jié)合實(shí)例代碼介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2025-12-12

