工作中禁止使用Executors快捷創(chuàng)建線程池原理詳解
問(wèn)題?
在很多公司(如阿里、華為等)的編程規(guī)范中,非常明確地禁止使用Executors快捷創(chuàng)建線程池,為什么呢?這里從源碼講起,介紹使用Executors工廠方法快捷創(chuàng)建線程池將會(huì)面臨的潛在問(wèn)題。


1.1 newFixedThreadPool的潛在問(wèn)題
基本使用
// 線程池
ExecutorService singleThreadExecutor = Executors.newFixedThreadPool(2);
// 批量添加線程
for (int i = 0; i < 7; i++) {
singleThreadExecutor.execute(new TargetTask());
// singleThreadExecutor.submit(new TargetTask());
}
Thread.sleep(1000);
// 線程池銷毀
singleThreadExecutor.shutdown();;
查看源碼
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
/**
* Creates a {@code LinkedBlockingQueue} with a capacity of
* {@link Integer#MAX_VALUE}.
*/
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
我們可以看出:
- corePoolSize(核心線程數(shù))=maximumPoolSize(最大線程數(shù))。
- LinkedBlockingQueue是一個(gè)無(wú)界隊(duì)列,如果提交的任務(wù)過(guò)快會(huì)造成任務(wù)大量的的堆積,消耗完服務(wù)器資源。
- 如果隊(duì)列很大,很有可能導(dǎo)致JVM出現(xiàn)OOM(Out Of Memory)異常,即內(nèi)存資源耗盡。
1.2 newSingleThreadExecutor的潛在問(wèn)題?
基本使用
// 線程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
// 批量添加線程
for (int i = 0; i < 5; i++) {
singleThreadExecutor.execute(new TargetTask());
// singleThreadExecutor.submit(new TargetTask());
}
Thread.sleep(1000);
// 線程池銷毀
singleThreadExecutor.shutdown();;
查看源碼
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
/**
* Creates a {@code LinkedBlockingQueue} with a capacity of
* {@link Integer#MAX_VALUE}.
*/
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
嘗試修改核心線程數(shù)
package ExecutorDemo.newSingleThreadExecutor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @description:
* @author: shu
* @createDate: 2022/11/1 10:45
* @version: 1.0
*/
public class UpdateSingleThreadExecutor {
public static void main(String[] args) {
//創(chuàng)建一個(gè)固定大小的線程池
ExecutorService fixedExecutorService =
Executors.newFixedThreadPool(1);
ThreadPoolExecutor threadPoolExecutor =
(ThreadPoolExecutor) fixedExecutorService;
System.out.println(threadPoolExecutor.getMaximumPoolSize());
//設(shè)置核心線程數(shù)
threadPoolExecutor.setCorePoolSize(8);
//創(chuàng)建一個(gè)單線程化的線程池
ExecutorService singleExecutorService =
Executors.newSingleThreadExecutor();
//轉(zhuǎn)換成普通線程池,會(huì)拋出運(yùn)行時(shí)異常 java.lang.ClassCastException
((ThreadPoolExecutor) singleExecutorService).setCorePoolSize(8);
}
}
我們可以看出:
- 單例存在,我們無(wú)法去修改核心線程數(shù),否則會(huì)造成異常處理。
- corePoolSize(核心線程數(shù))=maximumPoolSize(最大線程數(shù))=1 。
- LinkedBlockingQueue是一個(gè)無(wú)界隊(duì)列,如果提交的任務(wù)過(guò)快會(huì)造成任務(wù)大量的的堆積,消耗完服務(wù)器資源。
- 如果隊(duì)列很大,很有可能導(dǎo)致JVM出現(xiàn)OOM(Out Of Memory)異常,即內(nèi)存資源耗盡。
1.3 newCachedThreadPool的潛在問(wèn)題
基本使用
// 線程池
ExecutorService singleThreadExecutor = Executors.newCachedThreadPool();
// 批量添加線程
for (int i = 0; i < 7; i++) {
singleThreadExecutor.execute(new TargetTask());
// singleThreadExecutor.submit(new TargetTask());
}
Thread.sleep(1000);
// 線程池銷毀
singleThreadExecutor.shutdown();;
源碼分析
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
* Creates a {@code SynchronousQueue} with nonfair access policy.
*/
public SynchronousQueue() {
this(false);
}
- ThreadPoolExecutor標(biāo)準(zhǔn)構(gòu)造器創(chuàng)建一個(gè)核心線程數(shù)為0、最大線程數(shù)不設(shè)限制的線程池
- 理論上可緩存線程池可以擁有無(wú)數(shù)個(gè)工作線程,即線程數(shù)量幾乎無(wú)限制。
- 可緩存線程池的workQueue為SynchronousQueue同步隊(duì)列,這個(gè)隊(duì)列類似于一個(gè)接力棒,入隊(duì)出隊(duì)必須同時(shí)傳遞,正因?yàn)榭删彺婢€程池可以無(wú)限制地創(chuàng)建線程,不會(huì)有任務(wù)等待,所以才使用SynchronousQueue。
- 但是,maximumPoolSize的值為Integer.MAX_VALUE(非常大),可以認(rèn)為可以無(wú)限創(chuàng)建線程,如果任務(wù)提交較多,就會(huì)造成大量的線程被啟動(dòng),很有可能造成OOM異常,甚至導(dǎo)致CPU線程資源耗盡。
1.4 newScheduledThreadPool 潛在問(wèn)題
基本使用
// 線程池
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
// 批量添加線程
for (int i = 0; i < 7; i++) {
ScheduledFuture<?> future = service.scheduleWithFixedDelay(new TargetTask(), 0, 500, TimeUnit.MILLISECONDS);
}
Thread.sleep(1000);
// 線程池銷毀
service.shutdown();;
源碼分析
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
static class DelayedWorkQueue extends AbstractQueue<Runnable>
implements BlockingQueue<Runnable> {
private static final int INITIAL_CAPACITY = 16;
private RunnableScheduledFuture<?>[] queue =
new RunnableScheduledFuture<?>[INITIAL_CAPACITY];
private final ReentrantLock lock = new ReentrantLock();
private int size = 0;
private Thread leader = null;
private final Condition available = lock.newCondition();
}
maximumPoolSize為Integer.MAX_VALUE,表示線程數(shù)不設(shè)上限,其workQueue為一個(gè)DelayedWorkQueue實(shí)例,這是一個(gè)按到期時(shí)間升序排序的阻塞隊(duì)列。
1.5 總結(jié)

雖然Executors工廠類提供了構(gòu)造線程池的便捷方法,但是對(duì)于服務(wù)器程序而言,大家應(yīng)該杜絕使用這些便捷方法,而是直接使用線程池ThreadPoolExecutor的構(gòu)造器,從而有效避免由于使用無(wú)界隊(duì)列可能導(dǎo)致的內(nèi)存資源耗盡,或者由于對(duì)線程
以上就是工作中禁止使用Executors快捷創(chuàng)建線程池原理詳解的詳細(xì)內(nèi)容,更多關(guān)于禁止用Executors創(chuàng)建線程池的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
類似Object監(jiān)視器方法的Condition接口(詳解)
下面小編就為大家?guī)?lái)一篇類似Object監(jiān)視器方法的Condition接口(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05
Spring中實(shí)現(xiàn)定時(shí)調(diào)度的幾種方法
本篇文章主要介紹了Spring中實(shí)現(xiàn)定時(shí)調(diào)度示例,可以在無(wú)人值守的時(shí)候系統(tǒng)可以在某一時(shí)刻執(zhí)行某些特定的功能,有興趣的可以了解一下。2017-02-02
Java實(shí)現(xiàn)企業(yè)發(fā)放的獎(jiǎng)金根據(jù)利潤(rùn)提成問(wèn)題
這篇文章主要介紹了請(qǐng)利用數(shù)軸來(lái)分界,定位。注意定義時(shí)需把獎(jiǎng)金定義成長(zhǎng)整型,需要的朋友可以參考下2017-02-02
適用于Java初學(xué)者的學(xué)習(xí)路線圖
這篇文章主要介紹了學(xué)習(xí)Java的路線圖的五個(gè)必經(jīng)階段,還有一些作者的想法分享給大家,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09
Spring MVC文件上傳大小和類型限制以及超大文件上傳bug問(wèn)題
這篇文章主要介紹了Spring MVC文件上傳大小和類型限制以及超大文件上傳bug問(wèn)題,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-10-10
SpringBoot讀寫xml上傳到AWS存儲(chǔ)服務(wù)S3的示例
這篇文章主要介紹了SpringBoot讀寫xml上傳到S3的示例,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下2020-10-10

