java util下的并发包_jdk并发包下:使用java.util.concurrent.Executor线程池
多線程,線程池Executor的接口類圖:
其他都不重要,就ExecutorService是主要的:
基本上分為單純線程池和定時任務線程池:
說白了除了ForkJoinPool所有都繼承于ThreadPoolExecutor或者是對ThreadPoolExecutor的包裝類:
//構(gòu)造函數(shù)
public ThreadPoolExecutor(
int corePoolSize,//從0增加,直到維持不變的線程數(shù)
int maximumPoolSize,//最大創(chuàng)建的線程數(shù),比corePoolSize多出來的是多余線程數(shù),如果空閑會被釋放
long keepAliveTime,//空閑多久釋放
TimeUnit unit,//keepAliveTime的單位
BlockingQueue workQueue,//任務隊列對象
ThreadFactory threadFactory,//線程生產(chǎn)工廠
RejectedExecutionHandler handler//壓力大時如何處理拒絕任務
) {
}
上面構(gòu)造方法的各個參數(shù)的默認值:
corePoolSize:一般是1或者cpu核心數(shù)或者其他定量
maximumPoolSize:一般是0或者無限大
keepAliveTime:一般是0或者60s
unit:一般是TimeUnit.SECONDS
workQueue:一般是AbstractQueue 的子類
threadFactory:一般是DefaultThreadFactory implements ThreadFactory實例
Executors提供的類實例化方法:
int corePoolSize = 4;
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(corePoolSize);
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
ExecutorService executorService2 = Executors.newFixedThreadPool(corePoolSize);
ExecutorService executorService4 = Executors.newCachedThreadPool();
ExecutorService executorService3 = Executors.newWorkStealingPool(corePoolSize);
它們主要區(qū)別就是實例化的參數(shù)不同,比如固定的線程數(shù)是多少,并發(fā)高峰時的線程數(shù)能達到多少,空閑線程的存活時間不同,使用的保存任務的隊列類不同,繁忙時多余任務的拒絕策略,等等。
需要根據(jù)實際并發(fā)需求和特點來選擇不同的實例,或者自己實例化ThreadPoolExecutor直接使用。
創(chuàng)建好線程池后的調(diào)用就是submit(異步返回結(jié)果)和execute(不返回結(jié)果),其他方法 也簡單不說了。
說下最關(guān)心的ThreadPoolExecutor的參數(shù)對性能的影響:
過程如下:
當線程池初始化完成之后,而且當前線程數(shù)量小于corePoolSize,新來的任務直接通過創(chuàng)建線程來直接運行,并且線程運行后不會銷毀。
當線程池中正在運行的線程達到 corePoolSize 個時,不會繼續(xù)創(chuàng)建線程,任務會放到 taskQueue 中排隊等候;
當 taskQueue(阻塞隊列)的容量達到上限(即隊列中不能再加入任務線程了),而且設置的maximumPoolSize大于corePoolSize時,則新增額外線程來處理任務;
當 taskQueue 的容量達到上限,且 當前線程數(shù)poolSize 達到maximumPoolSize,那么線程池已經(jīng)達到極限,會根據(jù)飽和策略RejectedExecutionHandler處理新的任務。
測試驗證:
我設置核心線程數(shù)為4個,最大線程數(shù)為10個,隊列為10個滿容量,拒絕策略為拋異常:
int corePoolSize = 4;
int maximumPoolSize = 10;
long keepAliveTime = 10L;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue workQueue = new LinkedBlockingQueue<>(10);
ThreadFactory threadFactory = Executors.defaultThreadFactory();
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,
maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
,getActiveCount=0 ,getPoolSize=0 ,getTaskCount=0 ,getLargestPoolSize=0 ,getQueueSize=0
,getActiveCount=1 ,getPoolSize=1 ,getTaskCount=1 ,getLargestPoolSize=1 ,getQueueSize=0
,getActiveCount=2 ,getPoolSize=2 ,getTaskCount=2 ,getLargestPoolSize=2 ,getQueueSize=0
,getActiveCount=3 ,getPoolSize=3 ,getTaskCount=3 ,getLargestPoolSize=3 ,getQueueSize=0
,getActiveCount=4 ,getPoolSize=4 ,getTaskCount=4 ,getLargestPoolSize=4 ,getQueueSize=0
,getActiveCount=4 ,getPoolSize=4 ,getTaskCount=5 ,getLargestPoolSize=4 ,getQueueSize=1
,getActiveCount=4 ,getPoolSize=4 ,getTaskCount=6 ,getLargestPoolSize=4 ,getQueueSize=2
,getActiveCount=4 ,getPoolSize=4 ,getTaskCount=7 ,getLargestPoolSize=4 ,getQueueSize=3
,getActiveCount=4 ,getPoolSize=4 ,getTaskCount=8 ,getLargestPoolSize=4 ,getQueueSize=4
,getActiveCount=4 ,getPoolSize=4 ,getTaskCount=9 ,getLargestPoolSize=4 ,getQueueSize=5
,getActiveCount=4 ,getPoolSize=4 ,getTaskCount=10 ,getLargestPoolSize=4 ,getQueueSize=6
,getActiveCount=4 ,getPoolSize=4 ,getTaskCount=11 ,getLargestPoolSize=4 ,getQueueSize=7
,getActiveCount=4 ,getPoolSize=4 ,getTaskCount=12 ,getLargestPoolSize=4 ,getQueueSize=8
,getActiveCount=4 ,getPoolSize=4 ,getTaskCount=13 ,getLargestPoolSize=4 ,getQueueSize=9
,getActiveCount=4 ,getPoolSize=4 ,getTaskCount=14 ,getLargestPoolSize=4 ,getQueueSize=10
,getActiveCount=5 ,getPoolSize=5 ,getTaskCount=15 ,getLargestPoolSize=5 ,getQueueSize=10
,getActiveCount=6 ,getPoolSize=6 ,getTaskCount=16 ,getLargestPoolSize=6 ,getQueueSize=10
,getActiveCount=7 ,getPoolSize=7 ,getTaskCount=17 ,getLargestPoolSize=7 ,getQueueSize=10
,getActiveCount=8 ,getPoolSize=8 ,getTaskCount=18 ,getLargestPoolSize=8 ,getQueueSize=10
,getActiveCount=9 ,getPoolSize=9 ,getTaskCount=19 ,getLargestPoolSize=9 ,getQueueSize=10
,getActiveCount=10 ,getPoolSize=10 ,getTaskCount=20 ,getLargestPoolSize=10 ,getQueueSize=10
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.exemple.Test6$task@63947c6b rejected from java.util.concurrent.ThreadPoolExecutor@2b193f2d[Running, pool size = 10, active threads = 10, queued tasks = 10, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at com.exemple.Test6.main(Test6.java:30)
從上面輸出可以解析,
前4個任務,線程數(shù)一直增長從1到4,說明小于corePoolSize時一直創(chuàng)建線程
5到14個任務,線程數(shù)不變,隊列添加從1到10,說明大于corePoolSize時加入隊列等待
14到20個任務,隊列不變,線程數(shù)又從4增長到10,說明隊列滿時又創(chuàng)建線程直到達到最大線程數(shù)
第21個任務,拋出異常,因為最大10線程+最大隊列10容量<21,說明線程和隊列都達到最大值后,根據(jù)拒絕策略處理。
ThreadPoolExecutor線程池自身是線程安全的,但是對于執(zhí)行的任務并不保證線程安全,也沒有任何線程同步操作。需要用戶自己處理線程安全。
以后看性能如何再改下面的猜測:
個人就基于并發(fā)峰值、任務平均處理時間等等,猜測創(chuàng)建線程池各個參數(shù)的合理區(qū)間:
平均并發(fā)數(shù)
高峰并發(fā)數(shù)
任務執(zhí)行時間
合理參數(shù)配置:
說明
低
低
短
固定線程數(shù)=低合理值(CPU核心數(shù))
最大線程數(shù)=2倍CPU核心
空閑線程等待時間=60s
隊列容量=較大合理值
拒絕策略=隊列不會滿用不到
并發(fā)不高,不需要運行太多線程,
任務易處理,多出來全部放隊列即可
低
低
很長
固定線程數(shù)=低合理值(CPU核心數(shù))
最大線程數(shù)=較大合理值
空閑線程等待時間=60s
隊列容量=中等合理值
拒絕策略=實際需要
并發(fā)不高,不需要運行太多線程,
任務時間長,盡可能利用線程數(shù)
低
極高
短
固定線程數(shù)=低合理值
最大線程數(shù)=較高合理值
空閑線程等待時間=60s
隊列容量=防止內(nèi)存溢出較大值
拒絕策略=還不滿足,考慮買設備
平時并發(fā)不高,不需要運行太多線程,
任務易處理,并發(fā)高峰放隊列和新線程
很高
極高
短
固定線程數(shù)=中等合理值
最大線程數(shù)=較高合理值
空閑線程等待時間=60s
隊列容量=防止內(nèi)存溢出最大值
拒絕策略=重要任務不拋棄,最大化利用內(nèi)存和cpu資源
平時并發(fā)高,需要運行較多線程,
任務易處理,提升隊列容量
很高
極高
很長
固定線程數(shù)=中等合理值
最大線程數(shù)=較高合理值
空閑線程等待時間=60s
隊列容量=防止內(nèi)存溢出最大值
拒絕策略=重要任務不拋棄,最大化利用內(nèi)存和cpu資源
長時間任務,應該考慮另外使用任務調(diào)度容器來執(zhí)行。
參數(shù)設置依據(jù):
平常并發(fā)操作一般的話,線程數(shù)不是越高越好,相對于物理真實的線程數(shù)和線程時間片時間長度,此參數(shù)設置應該合理區(qū)間,不然實質(zhì)上會經(jīng)常切換線程調(diào)度,消耗切換時間和資源。
為了提高線程池處理能力,如果設置隊列容量過大,當真的有大批任務過來,可能導致內(nèi)存溢出。而且隊列過大,就不再觸發(fā)最大線程數(shù)這個設置,一直都是固定線程處理任務。
Executors提供的四種創(chuàng)建線程池的參數(shù)配置,都是特別對應不同場景的較好設置值,值得參考。
總結(jié)
以上是生活随笔為你收集整理的java util下的并发包_jdk并发包下:使用java.util.concurrent.Executor线程池的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用java模仿钉钉_java接入钉钉机器
- 下一篇: java list数据的更新,java