工作中禁止使用Executors快捷創(chuàng)建線程池原理詳解
問題?
在很多公司(如阿里、華為等)的編程規(guī)范中,非常明確地禁止使用Executors快捷創(chuàng)建線程池,為什么呢?這里從源碼講起,介紹使用Executors工廠方法快捷創(chuàng)建線程池將會面臨的潛在問題。
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是一個無界隊列,如果提交的任務過快會造成任務大量的的堆積,消耗完服務器資源。
- 如果隊列很大,很有可能導致JVM出現(xiàn)OOM(Out Of Memory)異常,即內存資源耗盡。
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)建一個固定大小的線程池 ExecutorService fixedExecutorService = Executors.newFixedThreadPool(1); ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) fixedExecutorService; System.out.println(threadPoolExecutor.getMaximumPoolSize()); //設置核心線程數(shù) threadPoolExecutor.setCorePoolSize(8); //創(chuàng)建一個單線程化的線程池 ExecutorService singleExecutorService = Executors.newSingleThreadExecutor(); //轉換成普通線程池,會拋出運行時異常 java.lang.ClassCastException ((ThreadPoolExecutor) singleExecutorService).setCorePoolSize(8); } }
我們可以看出:
- 單例存在,我們無法去修改核心線程數(shù),否則會造成異常處理。
- corePoolSize(核心線程數(shù))=maximumPoolSize(最大線程數(shù))=1 。
- LinkedBlockingQueue是一個無界隊列,如果提交的任務過快會造成任務大量的的堆積,消耗完服務器資源。
- 如果隊列很大,很有可能導致JVM出現(xiàn)OOM(Out Of Memory)異常,即內存資源耗盡。
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標準構造器創(chuàng)建一個核心線程數(shù)為0、最大線程數(shù)不設限制的線程池
- 理論上可緩存線程池可以擁有無數(shù)個工作線程,即線程數(shù)量幾乎無限制。
- 可緩存線程池的workQueue為SynchronousQueue同步隊列,這個隊列類似于一個接力棒,入隊出隊必須同時傳遞,正因為可緩存線程池可以無限制地創(chuàng)建線程,不會有任務等待,所以才使用SynchronousQueue。
- 但是,maximumPoolSize的值為Integer.MAX_VALUE(非常大),可以認為可以無限創(chuàng)建線程,如果任務提交較多,就會造成大量的線程被啟動,很有可能造成OOM異常,甚至導致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ù)不設上限,其workQueue為一個DelayedWorkQueue實例,這是一個按到期時間升序排序的阻塞隊列。
1.5 總結
雖然Executors工廠類提供了構造線程池的便捷方法,但是對于服務器程序而言,大家應該杜絕使用這些便捷方法,而是直接使用線程池ThreadPoolExecutor的構造器,從而有效避免由于使用無界隊列可能導致的內存資源耗盡,或者由于對線程
以上就是工作中禁止使用Executors快捷創(chuàng)建線程池原理詳解的詳細內容,更多關于禁止用Executors創(chuàng)建線程池的資料請關注腳本之家其它相關文章!
相關文章
類似Object監(jiān)視器方法的Condition接口(詳解)
下面小編就為大家?guī)硪黄愃芆bject監(jiān)視器方法的Condition接口(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05Java實現(xiàn)企業(yè)發(fā)放的獎金根據(jù)利潤提成問題
這篇文章主要介紹了請利用數(shù)軸來分界,定位。注意定義時需把獎金定義成長整型,需要的朋友可以參考下2017-02-02Spring MVC文件上傳大小和類型限制以及超大文件上傳bug問題
這篇文章主要介紹了Spring MVC文件上傳大小和類型限制以及超大文件上傳bug問題,非常具有實用價值,需要的朋友可以參考下2017-10-10SpringBoot讀寫xml上傳到AWS存儲服務S3的示例
這篇文章主要介紹了SpringBoot讀寫xml上傳到S3的示例,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下2020-10-10