工作中禁止使用Executors快捷創(chuàng)建線程池原理詳解
問題?
在很多公司(如阿里、華為等)的編程規(guī)范中,非常明確地禁止使用Executors快捷創(chuàng)建線程池,為什么呢?這里從源碼講起,介紹使用Executors工廠方法快捷創(chuàng)建線程池將會(huì)面臨的潛在問題。
1.1 newFixedThreadPool的潛在問題
基本使用
// 線程池 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è)無界隊(duì)列,如果提交的任務(wù)過快會(huì)造成任務(wù)大量的的堆積,消耗完服務(wù)器資源。
- 如果隊(duì)列很大,很有可能導(dǎo)致JVM出現(xiàn)OOM(Out Of Memory)異常,即內(nèi)存資源耗盡。
1.2 newSingleThreadExecutor的潛在問題?
基本使用
// 線程池 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); } }
我們可以看出:
- 單例存在,我們無法去修改核心線程數(shù),否則會(huì)造成異常處理。
- corePoolSize(核心線程數(shù))=maximumPoolSize(最大線程數(shù))=1 。
- LinkedBlockingQueue是一個(gè)無界隊(duì)列,如果提交的任務(wù)過快會(huì)造成任務(wù)大量的的堆積,消耗完服務(wù)器資源。
- 如果隊(duì)列很大,很有可能導(dǎo)致JVM出現(xiàn)OOM(Out Of Memory)異常,即內(nèi)存資源耗盡。
1.3 newCachedThreadPool的潛在問題
基本使用
// 線程池 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è)限制的線程池
- 理論上可緩存線程池可以擁有無數(shù)個(gè)工作線程,即線程數(shù)量幾乎無限制。
- 可緩存線程池的workQueue為SynchronousQueue同步隊(duì)列,這個(gè)隊(duì)列類似于一個(gè)接力棒,入隊(duì)出隊(duì)必須同時(shí)傳遞,正因?yàn)榭删彺婢€程池可以無限制地創(chuàng)建線程,不會(huì)有任務(wù)等待,所以才使用SynchronousQueue。
- 但是,maximumPoolSize的值為Integer.MAX_VALUE(非常大),可以認(rèn)為可以無限創(chuàng)建線程,如果任務(wù)提交較多,就會(huì)造成大量的線程被啟動(dòng),很有可能造成OOM異常,甚至導(dǎo)致CPU線程資源耗盡。
1.4 newScheduledThreadPool 潛在問題
基本使用
// 線程池 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)造器,從而有效避免由于使用無界隊(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ī)硪黄愃芆bject監(jiān)視器方法的Condition接口(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05Spring中實(shí)現(xiàn)定時(shí)調(diào)度的幾種方法
本篇文章主要介紹了Spring中實(shí)現(xiàn)定時(shí)調(diào)度示例,可以在無人值守的時(shí)候系統(tǒng)可以在某一時(shí)刻執(zhí)行某些特定的功能,有興趣的可以了解一下。2017-02-02Java實(shí)現(xiàn)企業(yè)發(fā)放的獎(jiǎng)金根據(jù)利潤提成問題
這篇文章主要介紹了請(qǐng)利用數(shù)軸來分界,定位。注意定義時(shí)需把獎(jiǎng)金定義成長整型,需要的朋友可以參考下2017-02-02適用于Java初學(xué)者的學(xué)習(xí)路線圖
這篇文章主要介紹了學(xué)習(xí)Java的路線圖的五個(gè)必經(jīng)階段,還有一些作者的想法分享給大家,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09Spring MVC文件上傳大小和類型限制以及超大文件上傳bug問題
這篇文章主要介紹了Spring MVC文件上傳大小和類型限制以及超大文件上傳bug問題,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-10-10SpringBoot讀寫xml上傳到AWS存儲(chǔ)服務(wù)S3的示例
這篇文章主要介紹了SpringBoot讀寫xml上傳到S3的示例,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下2020-10-10