為什么阿里禁止使用Executors去創(chuàng)建線程池
在阿里巴巴的《Java開發(fā)手冊》中有明確規(guī)定:
【強制】線程池不允許使用
Executors去創(chuàng)建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學(xué)更加明確線程池的運行規(guī)則,規(guī)避資源耗盡的風(fēng)險。
阿里禁止使用Executors并不是說這個類的方法本身有致命的Bug,而是因為它隱藏了資源耗盡的風(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(因為核心線程數(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)緩慢:
- 即使沒有達到OOM,任務(wù)在隊列中排隊的時間也會非常長。一個任務(wù)可能被提交后很久才得到執(zhí)行,導(dǎo)致系統(tǒng)整體響應(yīng)時間變得不可接受。
3、問題定位困難:
- 由于隊列是無界的,系統(tǒng)在OOM之前可能看起來“正常”運行,只是有點慢。當(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ù)(如隊列類型和大小)。通過強制使用ThreadPoolExecutor構(gòu)造函數(shù),開發(fā)者必須顯式地設(shè)置核心線程數(shù)、最大線程數(shù)、隊列容量、拒絕策略等。這個過程迫使開發(fā)者去思考線程池的配置是否合理,加深對線程池運行機制的理解。
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)險意識,強制通過 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)文章
分布式系統(tǒng)下調(diào)用鏈追蹤技術(shù)面試題
這篇文章主要為大家介紹了分布式系統(tǒng)下調(diào)用鏈追蹤技術(shù)面試問題合集,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-03-03
spring整合JMS實現(xiàn)同步收發(fā)消息(基于ActiveMQ的實現(xiàn))
本篇文章主要介紹了spring整合JMS實現(xiàn)同步收發(fā)消息(基于ActiveMQ的實現(xiàn)),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-10-10
Java數(shù)據(jù)結(jié)構(gòu)之優(yōu)先級隊列(堆)圖文詳解
優(yōu)先級隊列是比棧和隊列更專用的結(jié)構(gòu),在多數(shù)情況下都非常有用,下面這篇文章主要給大家介紹了關(guān)于Java數(shù)據(jù)結(jié)構(gòu)之優(yōu)先級隊列(堆)的相關(guān)資料,需要的朋友可以參考下2022-03-03
一文詳解SpringBoot中CommandLineRunner接口
Spring Boot的CommandLineRunner接口是一個函數(shù)式接口,用于在Spring Boot應(yīng)用程序啟動后執(zhí)行一些初始化操作,它提供了一個run方法,該方法在應(yīng)用程序啟動后被調(diào)用,本文給大家詳細(xì)介紹了SpringBoot中CommandLineRunner接口,需要的朋友可以參考下2023-10-10
借助Maven搭建Hadoop開發(fā)環(huán)境的最詳細(xì)教程分享
在Maven插件的幫助下,VSCode寫Java其實非常方便,所以本文就來和大家詳細(xì)講講如何借助maven用VScode搭建Hadoop開發(fā)環(huán)境,需要的可以參考下2023-05-05
Java中使用ForkJoinPool的實現(xiàn)示例
ForkJoinPool是一個功能強大的Java類,用于處理計算密集型任務(wù),本文主要介紹了Java中使用ForkJoinPool的實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下2023-09-09

