執行緒池的阻塞佇列的理解
常見的三種工廠類執行緒池
1、newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
由於佇列緩衝區為空,每來一個任務時,都會在必要時新建執行緒執行任務直到到達Integer.MAX_VALUE,這就有可能導致大量的執行緒被建立,進而系統癱瘓
2、newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
//超過nThreads個執行緒後會無限制的往阻塞佇列放入執行緒
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
3、newSingleThreadExecutor
建立一個單執行緒化的執行緒池
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
//超過1個執行緒後會無限制的往阻塞佇列放入執行緒
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
4、newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
執行緒池常用阻塞佇列
1.SynchronousQueue
private static ExecutorService cachedThreadPool = new ThreadPoolExecutor(4, Runtime.getRuntime().availableProcessors() * 2, 0, TimeUnit.MILLISECONDS, new SynchronousQueue<>(), r -> new Thread(r, "ThreadTest"));
SynchronousQueue沒有容量,是無緩衝等待佇列,是一個不儲存元素的阻塞佇列,會直接將任務交給消費者,必須等佇列中的新增元素被消費後才能繼續新增新的元素。
擁有公平(FIFO)和非公平(LIFO)策略,非公平側羅會導致一些資料永遠無法被消費的情況
使用SynchronousQueue阻塞佇列一般用於構造newCachedThreadPool
2.LinkedBlockingQueue
private static ExecutorService cachedThreadPool = new ThreadPoolExecutor(4, Runtime.getRuntime().availableProcessors() * 2, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), r -> new Thread(r, "ThreadTest"));
LinkedBlockingQueue是一個無界快取等待佇列。當前執行的執行緒數量達到corePoolSize的數量時,剩餘的元素會在阻塞佇列裡等待。(所以在使用此阻塞佇列時maximumPoolSizes就相當於無效了),每個執行緒完全獨立於其他執行緒。生產者和消費者使用獨立的鎖來控制資料的同步,即在高併發的情況下可以並行操作佇列中的資料。
3.ArrayBlockingQueue
private static ExecutorService cachedThreadPool = new ThreadPoolExecutor(4, Runtime.getRuntime().availableProcessors() * 2, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(32), r -> new Thread(r, "ThreadTest"));
ArrayBlockingQueue是一個有界快取等待佇列,可以指定快取佇列的大小,當正在執行的執行緒數等於corePoolSize時,多餘的元素快取在ArrayBlockingQueue佇列中等待有空閒的執行緒時繼續執行,當ArrayBlockingQueue已滿時,加入ArrayBlockingQueue失敗,會開啟新的執行緒去執行,當執行緒數已經達到最大的maximumPoolSizes時,再有新的元素嘗試加入ArrayBlockingQueue時會報錯。
執行緒池拒絕策略
1、CallerRunsPolicy:執行緒呼叫執行該任務的 execute 本身。此策略提供簡單的反饋控制機制,能夠減緩新任務的提交速度。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); }}
這個策略顯然不想放棄執行任務。但是由於池中已經沒有任何資源了,那麼就直接使用呼叫該execute的執行緒本身來執行。(開始我總不想丟棄任務的執行,但是對某些應用場景來講,
很有可能造成當前執行緒也被阻塞。如果所有執行緒都是不能執行的,很可能導致程式沒法繼續跑了。需要視業務情景而定吧。)
2、AbortPolicy:處理程式遭到拒絕將丟擲執行時 RejectedExecutionException
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {throw new RejectedExecutionException();}
這種策略直接丟擲異常,丟棄任務。(jdk預設策略,佇列滿併執行緒滿時直接拒絕新增新任務,並丟擲異常,所以說有時候放棄也是一種勇氣,為了保證後續任務的正常進行,丟棄一些也是可以接收的,記得做好記錄)
3、DiscardPolicy:不能執行的任務將被刪除
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {} 這種策略和AbortPolicy幾乎一樣,也是丟棄任務,只不過他不丟擲異常。
4、DiscardOldestPolicy:如果執行程式尚未關閉,則位於工作佇列頭部的任務將被刪除,然後重試執行程式(如果再次失敗,則重複此過程)
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) {e.getQueue().poll();e.execute(r); }}
該策略就稍微複雜一些,在pool沒有關閉的前提下首先丟掉快取在隊
如何合理設定執行緒池佇列長度
tomcat、Dubbo 等業界成熟的產品是如何設定執行緒佇列,我們主要分析下這兩個中介軟體
1.JDK執行緒池策略
1. 如果此時執行緒池中的數量小於corePoolSize,即使執行緒池中的執行緒都處於空閒狀態,也要建立新的執行緒來處理被新增的任務。
2. 如果此時執行緒池中的數量大於等於corePoolSize,但是緩衝佇列 workQueue未滿,那麼任務被放入緩衝佇列。
3. 如果此時執行緒池中的數量大於corePoolSize,緩衝佇列workQueue滿,並且執行緒池中的數量小於maximumPoolSize,建新的執行緒來處理新增的任務。
4. 如果此時執行緒池中的數量大於corePoolSize,緩衝佇列workQueue滿,並且執行緒池中的數量等於maximumPoolSize,那麼通過 handler所指定的策略來處理此任務。也就是處理任務的優先順序為:核心執行緒corePoolSize、任務佇列workQueue、最大執行緒maximumPoolSize,如果三者都滿了,使用handler處理被拒絕的任務。
5. 當執行緒池中的執行緒數量大於corePoolSize時,如果某執行緒空閒時間超過keepAliveTime,執行緒將被終止。這樣,執行緒池可以動態的調整池中的執行緒數。
2.Tomcat執行緒池策略
Tomcat的執行緒池佇列是無限長度的,但是執行緒池會一直建立到maximumPoolSize,然後才把請求放入等待佇列中
tomcat 任務佇列org.apache.tomcat.util.threads.TaskQueue其繼承與LinkedBlockingQueue,覆寫offer方法。
@Override
public boolean offer(Runnable o) {
//we can't do any checks
if (parent==null) return super.offer(o);
//we are maxed out on threads, simply queue the object
if (parent.getPoolSize() == parent.getMaximumPoolSize()) return super.offer(o);
//we have idle threads, just add it to the queue
if (parent.getSubmittedCount()<(parent.getPoolSize())) return super.offer(o);
//執行緒個數小於MaximumPoolSize會建立新的執行緒。
//if we have less threads than maximum force creation of a new thread
if (parent.getPoolSize()<parent.getMaximumPoolSize()) return false;
//if we reached here, we need to add it to the queue
return super.offer(o);
}
3.Dubbo執行緒池策略
Dubbo 提供3種執行緒池模型即:FixedThreadPool、CachedThreadPool(客戶端預設的)、LimitedThreadPool(服務端預設的),從原始碼可以看出,其預設的佇列長度都是0,當佇列長度為0 ,其使用是無緩衝的佇列SynchronousQueue,當執行執行緒超過maximumPoolSize則拒絕請求。
總結
執行緒池的任務佇列本來起緩衝作用,但是如果設定的不合理會導致執行緒池無法擴容至max,這樣無法發揮多執行緒的能力,導致一些服務響應變慢。
佇列長度要看具體使用場景,取決服務端處理能力以及客戶端能容忍的超時時間等
建議採用tomcat的處理方式,core與max一致,先擴容到max再放佇列,不過佇列長度要根據使用場景設定一個上限值,如果響應時間要求較高的系統可以設定為0。
相關文章
- java多執行緒:執行緒池原理、阻塞佇列Java執行緒佇列
- 探討阻塞佇列和執行緒池原始碼佇列執行緒原始碼
- 原始碼剖析ThreadPoolExecutor執行緒池及阻塞佇列原始碼thread執行緒佇列
- Java阻塞佇列執行緒集控制的實現Java佇列執行緒
- java執行緒池-工作佇列workQueueJava執行緒佇列
- 執行緒池與工作佇列(轉)執行緒佇列
- Java執行緒(篇外篇):阻塞佇列BlockingQueueJava執行緒佇列BloC
- java 執行緒池執行緒忙碌且阻塞佇列也滿了時給一個拒接的詳細報告Java執行緒佇列
- Java BlockingQueue 阻塞佇列[用於多執行緒]JavaBloC佇列執行緒
- 深入淺出Java多執行緒(十三):阻塞佇列Java執行緒佇列
- java-執行緒池佇列飽和策略Java執行緒佇列
- java多執行緒8:阻塞佇列與Fork/Join框架Java執行緒佇列框架
- 證明執行緒池ThreadPoolExecutor的核心執行緒數,最大執行緒數,佇列長度的關係執行緒thread佇列
- 多執行緒程式設計-分析阻塞佇列的原始碼實現執行緒程式設計佇列原始碼
- 深入理解執行緒池的執行流程執行緒
- Java幾種執行緒池及任務佇列Java執行緒佇列
- 執行緒的阻塞執行緒
- 阻塞佇列一——java中的阻塞佇列佇列Java
- 踩坑 Spring Cloud Hystrix 執行緒池佇列配置SpringCloud執行緒佇列
- C# 使用執行緒池佇列(學習筆記)C#執行緒佇列筆記
- 執行緒和執行緒池的理解與java簡單例子執行緒Java單例
- 多執行緒系列(十三) -一文帶你搞懂阻塞佇列執行緒佇列
- 多執行緒,執行緒類三種方式,執行緒排程,執行緒同步,死鎖,執行緒間的通訊,阻塞佇列,wait和sleep區別?執行緒佇列AI
- 執行緒池監控2-監控執行緒池狀態、執行緒數量和佇列任務數量等執行緒佇列
- 漫畫理解執行緒池執行緒
- 最全java多執行緒總結3——瞭解阻塞佇列和執行緒安全集合不Java執行緒佇列
- 對執行緒、協程和同步非同步、阻塞非阻塞的理解執行緒非同步
- 主佇列&主執行緒佇列執行緒
- 淺談執行緒池(上):執行緒池的作用及CLR執行緒池執行緒
- java自帶執行緒池和佇列詳細講解Java執行緒佇列
- 多執行緒:執行緒池理解和使用總結執行緒
- 佇列、阻塞佇列佇列
- 執行緒池中的最大執行緒數、核心執行緒數和佇列大小的合理設定執行緒佇列
- python 的執行緒池如何獲取 work 佇列中的訊息數量Python執行緒佇列
- 執行緒的建立及執行緒池執行緒
- 淺談執行緒池(中):獨立執行緒池的作用及IO執行緒池執行緒
- 詳談執行緒池的理解和應用執行緒
- 如何優雅的使用和理解執行緒池執行緒