爲何阿里巴巴要禁用Executors建立線程池?看阿里巴巴開發手冊併發編程這塊有一條:線程池不容許使用Executors去建立,而是經過ThreadPoolExecutor的方式,經過源碼分析禁用的緣由
管理一組工做線程。經過線程池複用線程有如下幾點優勢:java
OutOfMemoryError
【簡稱OOM】根據返回的對象類型建立線程池能夠分爲三類:編程
由於這些建立線程池的靜態方法都是返回ThreadPoolExecutor
對象,和咱們手動建立ThreadPoolExecutor
對象的區別就是咱們不須要本身傳構造函數的參數。ThreadPoolExecutor
的構造函數共有四個,但最終調用的都是同一個:緩存
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
構造函數參數說明:併發
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1ggm9avnt4pj30lp0ak74g.jpg" alt="image-20200205095809050" style="zoom:50%;" />ide
執行邏輯說明:函數
corePoolSize
參數有關,未滿則建立線程執行任務workQueue
參數有關,若未滿則加入隊列中maximumPoolSize
參數有關,若未滿建立線程執行任務handler
參數有關Executors
建立返回ThreadPoolExecutor對象的方法共有三種:工具
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
CachedThreadPool
是一個根據須要建立新線程的線程池當一個任務提交時,corePoolSize
爲0不建立核心線程,SynchronousQueue
是一個不存儲元素的隊列,能夠理解爲隊裏永遠是滿的,所以最終會建立非核心線程來執行任務。對於非核心線程空閒60s時將被回收。**由於Integer.MAX_VALUE
很是大,能夠認爲是能夠無限建立線程的,在資源有限的狀況下容易引發OOM異常源碼分析
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
SingleThreadExecutor
是單線程線程池,只有一個核心線程post
當一個任務提交時,首先會建立一個核心線程來執行任務,若是超過核心線程的數量,將會放入隊列中,由於LinkedBlockingQueue
是長度爲Integer.MAX_VALUE
的隊列,能夠認爲是無界隊列,所以往隊列中能夠插入無限多的任務,在資源有限的時候容易引發OOM
異常,同時由於無界隊列,maximumPoolSize
和keepAliveTime
參數將無效,壓根就不會建立非核心線程性能
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
FixedThreadPool
是固定核心線程的線程池,固定核心線程數由用戶傳入
SingleThreadExecutor
相似,惟一的區別就是核心線程數不一樣,而且因爲**使用的是LinkedBlockingQueue
,在資源有限的時候容易引發OOM
異常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++)); } } }
使用Executors
建立的CachedThreadPool
,往線程池中無限添加線程 在啓動測試類以前先將JVM
內存調整小一點,否則很容易將電腦跑出問題,在idea
裏:Run
-> Edit Configurations
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1ggm9iwblb8j30tw0i60tn.jpg" alt="image-20200205095809050" style="zoom:50%;" />
建立到3w多個線程的時候開始報OOM
錯誤
另外兩個線程池就不作測試了,測試方法一致,只是建立的線程池不同
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1ggm9ugaj8ij315c0juaaq.jpg" alt="image-20200205095809050" style="zoom:30%" />
CPU
數量 + 1,CPU
數量能夠根據Runtime.availableProcessors
方法獲取CPU
數量 CPU
利用率 (1 + 線程等待時間/線程CPU時間)CPU
密集型和IO
密集型,而後分別使用不一樣的線程池去處理,從而使每一個線程池能夠根據各自的工做負載來調整拒絕策略 => 默認採用的是AbortPolicy拒絕策略,直接在程序中拋出 RejectedExecutionException 異常【由於是運行時異常,不強制catch】,這種處理方式不夠優雅。處理拒絕策略有如下幾種比較推薦:
RejectedExecutionException
異常,在捕獲異常中對任務進行處理。針對默認拒絕策略CallerRunsPolicy
拒絕策略,該策略會將任務交給調用execute的線程執行【通常爲主線程】,此時主線程將在一段時間內不能提交任何任務,從而使工做線程處理正在執行的任務。此時提交的線程將被保存在TCP
隊列中,TCP隊列滿將會影響客戶端,這是一種平緩的性能下降RejectedExecutionHandler
接口便可DiscardPolicy
和DiscardOldestPolicy
拒絕策略將任務丟棄也是能夠的若是使用Executors的靜態方法建立ThreadPoolExecutor
對象,能夠經過使用Semaphore
對任務的執行進行限流也能夠避免出現OOM
異常
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build() ); executorService.execute(() -> System.out.println("run"));
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build(); ExecutorService executor = new ThreadPoolExecutor( 5, 200, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy() ); executor.submit(() -> System.out.println(Thread.currentThread().getName() + "run")); executor.shutdown();
https://juejin.im/post/5dc41c...
https://my.oschina.net/u/4440...
本文由博客羣發一文多發等運營工具平臺 OpenWrite 發佈