為什么阿里巴巴要禁用 Executors 創(chuàng)建線程池?
閱讀本文大概需要 5 分鐘。
看阿里巴巴開發(fā)手冊并發(fā)編程這塊有一條:線程池不允許使用Executors去創(chuàng)建,而是通過ThreadPoolExecutor的方式,通過源碼分析禁用的原因
寫在前面
線程池的定義 Executors創(chuàng)建線程池的幾種方式 ThreadPoolExecutor對象 線程池執(zhí)行任務邏輯和線程池參數(shù)的關(guān)系 Executors創(chuàng)建返回ThreadPoolExecutor對象 OOM異常測試 如何定義線程池參數(shù)
線程池的定義
管理一組工作線程。通過線程池復用線程有以下幾點優(yōu)點:
減少資源創(chuàng)建 => 減少內(nèi)存開銷,創(chuàng)建線程占用內(nèi)存 降低系統(tǒng)開銷 => 創(chuàng)建線程需要時間,會延遲處理的請求 提高穩(wěn)定穩(wěn)定性 => 避免無限創(chuàng)建線程引起的OutOfMemoryError【簡稱OOM】
Executors創(chuàng)建線程池的方式
根據(jù)返回的對象類型創(chuàng)建線程池可以分為三類:
創(chuàng)建返回ThreadPoolExecutor對象 創(chuàng)建返回ScheduleThreadPoolExecutor對象 創(chuàng)建返回ForkJoinPool對象
ThreadPoolExecutor對象
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize => 線程池核心線程數(shù)量 maximumPoolSize => 線程池最大數(shù)量 keepAliveTime => 空閑線程存活時間 unit => 時間單位 workQueue => 線程池所使用的緩沖隊列 threadFactory => 線程池創(chuàng)建線程使用的工廠 handler => 線程池對拒絕任務的處理策略
線程池執(zhí)行任務邏輯和線程池參數(shù)的關(guān)系

判斷核心線程數(shù)是否已滿,核心線程數(shù)大小和corePoolSize參數(shù)有關(guān),未滿則創(chuàng)建線程執(zhí)行任務 若核心線程池已滿,判斷隊列是否滿,隊列是否滿和workQueue參數(shù)有關(guān),若未滿則加入隊列中 若隊列已滿,判斷線程池是否已滿,線程池是否已滿和maximumPoolSize參數(shù)有關(guān),若未滿創(chuàng)建線程執(zhí)行任務 若線程池已滿,則采用拒絕策略處理無法執(zhí)執(zhí)行的任務,拒絕策略和handler參數(shù)有關(guān)
Executors創(chuàng)建返回ThreadPoolExecutor對象
Executors#newCachedThreadPool => 創(chuàng)建可緩存的線程池 Executors#newSingleThreadExecutor => 創(chuàng)建單線程的線程池 Executors#newFixedThreadPool => 創(chuàng)建固定長度的線程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
corePoolSize => 0,核心線程池的數(shù)量為0 maximumPoolSize => Integer.MAX_VALUE,可以認為最大線程數(shù)是無限的 keepAliveTime => 60L unit => 秒 workQueue => SynchronousQueue
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
corePoolSize => 1,核心線程池的數(shù)量為1 maximumPoolSize => 1,只可以創(chuàng)建一個非核心線程 keepAliveTime => 0L unit => 秒 workQueue => LinkedBlockingQueue
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
corePoolSize => 1,核心線程池的數(shù)量為1 maximumPoolSize => 1,只可以創(chuàng)建一個非核心線程 keepAliveTime => 0L unit => 秒 workQueue => LinkedBlockingQueue 它和SingleThreadExecutor類似,唯一的區(qū)別就是核心線程數(shù)不同,并且由于使用的是LinkedBlockingQueue,在資源有限的時候容易引起OOM異常
總結(jié):
FixedThreadPool和SingleThreadExecutor => 允許的請求隊列長度為Integer.MAX_VALUE,可能會堆積大量的請求,從而引起OOM異常 CachedThreadPool => 允許創(chuàng)建的線程數(shù)為Integer.MAX_VALUE,可能會創(chuàng)建大量的線程,從而引起OOM異常
OOM異常測試
public class TaskTest {
public static void main(String[] args) {
ExecutorService es = Executors.newCachedThreadPool();
int i = 0;
while (true) {
es.submit(new Task(i++));
}
}
}

-Xms10M => Java Heap內(nèi)存初始化值 -Xmx10M => Java Heap內(nèi)存最大值
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"
Disconnected from the target VM, address: '127.0.0.1:60416', transport: 'socket'
如何定義線程池參數(shù)
在程序中捕獲RejectedExecutionException異常,在捕獲異常中對任務進行處理。針對默認拒絕策略 使用CallerRunsPolicy拒絕策略,該策略會將任務交給調(diào)用execute的線程執(zhí)行【一般為主線程】,此時主線程將在一段時間內(nèi)不能提交任何任務,從而使工作線程處理正在執(zhí)行的任務。此時提交的線程將被保存在TCP隊列中,TCP隊列滿將會影響客戶端,這是一種平緩的性能降低 自定義拒絕策略,只需要實現(xiàn)RejectedExecutionHandler接口即可 如果任務不是特別重要,使用DiscardPolicy和DiscardOldestPolicy拒絕策略將任務丟棄也是可以的
推薦閱讀:
16歲黑客入侵騰訊系統(tǒng)入獄,后做手游創(chuàng)業(yè)賺數(shù)億,如今31歲的他在哪里?
最近面試BAT,整理一份面試資料《Java面試BATJ通關(guān)手冊》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。
朕已閱 
評論
圖片
表情

