點關注,不迷路;持續更新Java相關技術及資訊!!!常見線程池
線程池參數的意義及工做原理java
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
//...
}
複製代碼
線程池有這麼幾個重要的參數緩存
corePoolSize: 線程池裏的核心線程數量bash
maximumPoolSize: 線程池裏容許有的最大線程數量分佈式
keepAliveTime: 若是 當前線程數量 > corePoolSize,多出來的線程會在keepAliveTime以後就被釋放掉源碼分析
unit: keepAliveTime的時間單位,好比分鐘,小時等學習
workQueue: 隊列ui
threadFactory: 每當須要建立新的線程放入線程池的時候,就是經過這個線程工廠來建立的url
handler: 就是說當線程,隊列都滿了,以後採起的策略,好比拋出異常等策略spa
那麼咱們假設來一組參數練習一下這個參數的意義線程
corePoolSize:1
mamximumPoolSize:3
keepAliveTime:60s
workQueue:ArrayBlockingQueue,有界阻塞隊列,隊列大小是4
handler:默認的策略,拋出來一個ThreadPoolRejectException複製代碼
1.一開始有一個線程變量poolSize維護當前線程數量.此時poolSize=0
2.此時來了一個任務.須要建立線程.poolSize(0) < corePoolSize(1),那麼直接建立線程
3.此時來了一個任務.須要建立線程.poolSize(1) >= corePoolSize(1),此時隊列沒滿,那麼就丟到隊列中去
4.若是隊列也滿了,可是poolSize < mamximumPoolSize,那麼繼續建立線程
5.若是poolSize == maximumPoolSize,那麼此時再提交一個一個任務,就要執行handler,默認就是拋出異常
6.此時線程池有3個線程(poolSize == maximumPoolSize(3)),假如都處於空閒狀態,可是corePoolSize=1,那麼就有(3-1 =2),那麼這超出的2個空閒線程,空閒超過60s,就會給回收掉.
以上,就是線程池參數意義及工做原理
線程池參數設計上的思考
知道了以上的原理,那麼咱們看看常見的兩個線程池FixedThreadExecutor和CachedThreadExecutor的參數設計
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
複製代碼
那麼問題來了
1.爲何FixedThreadExecutor的corePoolSize和mamximumPoolSize要設計成同樣的?
2.爲何CachedThreadExecutor的mamximumPoolSize要設計成接近無限大的?
敲黑板劃重點
仍是前面那句話,咱們看源碼,並非大段大段的源碼打上註釋,最重要的是通過深度思考,明白做者設計的意圖,這也就是爲何市場上有這麼多源碼解析文章,咱們依然還要關注一下肥朝(賣個萌)
若是你對上面的線程池的原理,參數有了清晰的認識,天然很快就能明白這個設計思路.
好比問題一,由於線程池是先判斷corePoolSize,再判斷workQueue,最後判斷mamximumPoolSize,然而LinkedBlockingQueue是無界隊列,因此他是達不到判斷mamximumPoolSize這一步的,因此mamximumPoolSize成多少,並無多大所謂
好比問題二:咱們來看看SynchronousQueue的註釋:
從我圈的這幾個小學英文單詞都知道,這個隊列的容量是很小的,若是mamximumPoolSize不設計得很大,那麼就很容易動不動就拋出異常
線程池使用上的建議
原理明白了,設計思想咱們也明白了,代碼要怎麼寫.光理論還不行,也就是說,咱們在項目中,線程池究竟要怎麼用?那麼咱們來看一下阿里手冊,看到這個強制相信不用我多說什麼
Dubbo線程池
那麼咱們來看看Dubbo官方文檔,一直強調,官方文檔纔是最好的學習資料.
迴歸問題
那麼回到咱們前面遇到的問題.咱們看了官方文檔說Dubbo默認(缺省)用線程池是fixed,那麼咱們第一反應,從前面各類分析原理也得知了,FixedThreadPool的隊列是很大的,他根本達不到第三個判斷條件mamximumPoolSize,達不到第三個條件,也就不會觸發handle拋出異常.那前面那個壓測問題的異常怎麼來的。
直入源碼
這種問題.搜索是很差使了,由於根本很差搜索.那麼咱們只好直入源碼了
@SPI("fixed")
public interface ThreadPool {
/**
* 線程池
*
* @param url 線程參數
* @return 線程池
*/
@Adaptive({Constants.THREADPOOL_KEY})
Executor getExecutor(URL url);
}
public class FixedThreadPool implements ThreadPool {
public Executor getExecutor(URL url) {
String name = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);
return new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS,
queues == 0 ? new SynchronousQueue<Runnable>() :
(queues < 0 ? new LinkedBlockingQueue<Runnable>()
: new LinkedBlockingQueue<Runnable>(queues)),
new NamedThreadFactory(name, true), new AbortPolicyWithReport(name, url));
}
}
複製代碼
此時咱們發現,Dubbo裏面的FixedThreadPool和newFixedThreadPool建立出來的FixedThreadPool參數是不同的.默認狀況下,Dubbo的FixedThreadPool中,maximumPoolSize = 200,隊列是容量很小的SynchronousQueue.因此當線程超過200的時候,就會拋出異常.這個和咱們上面分析的原理是一致的.
其實換個角度想,規範手冊都是阿里出的,阿里手冊都強制說要用ThreadPoolExecutor的方式來建立了,並且還給你分析了無界隊列的風險,那麼Dubbo官方文檔說的fixed又怎麼會是Executors建立的無界隊列這種呢?
知道了線程池的原理和異常的根源以後,咱們徹底能夠根據業務特色的不一樣,自定義線程池參數,來避免這類異常的頻繁發生.亦或者改變Dubbo默認的線程模型,從aLL改爲message等,這個就要結合實際的業務狀況了。
關注、轉發、評論天天分享java 知識,贈送Spring源碼分析、Dubbo、Redis、Netty、zookeeper、Spring cloud、分佈式資料