引用自:http://blog.iluckymeeting.com/2018/01/06/JavaThreadPool/
線程池管理原則
- 降低系統(tǒng)資源消耗
- 提高系統(tǒng)響應(yīng)速度
- 提高線程的可管理性
- 提高系統(tǒng)的穩(wěn)定性
Java線程池實現(xiàn)原理
Java通過線程池的工廠類Executors創(chuàng)建線程池今缚,底層最終都是實例化了ThreadPoolExecutor來創(chuàng)建線程池
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
- corePoolSize
線程池中的核心線程數(shù)参淹,即使線程當(dāng)前是空閑狀態(tài),也會保留在線程池中不會被回收围小,除非設(shè)置了allowCoreThreadTimeOut參數(shù)
- maximumPoolSize
線程池中允許創(chuàng)建的最大線程池數(shù)量
- keepAliveTime
線程最大空閑時間宪迟,如果線程池中的線程數(shù)量大于核心線程數(shù)量設(shè)置corePoolSize酣衷,則此參數(shù)起作用。當(dāng)空閑線程等待任務(wù)到來的時間大于這個值次泽,線程將被回收穿仪。
- unit
指的時keepAliveTime的單位
- workQueue
如果線程池中已經(jīng)沒有空閑線程了,而線程數(shù)量已經(jīng)達到了maximumPoolSize時,意味著不能再創(chuàng)建新的線程意荤,這時新的任務(wù)將被保存到阻塞隊列中等待有空閑的線程出現(xiàn)啊片。
- threadFactory
創(chuàng)建線程的工廠類
- handler
當(dāng)暫存任務(wù)的阻塞隊列被填滿時,新到來的任務(wù)既不能在阻塞隊列里暫存等待處理玖像,也沒有空閑的線程來處理紫谷,需要要指定handler來處理這些任務(wù)
總結(jié)一下Java線程池的原理:
當(dāng)有任務(wù)需要處理時,先在線程池中尋找空閑線程處理捐寥,如果沒有空閑線程笤昨,而此時線程池中的線程數(shù)量沒有達到corePoolSize,則創(chuàng)建新的線程處理任務(wù);如果此時線程池中的線程數(shù)量已經(jīng)達到了corePoolSize握恳,則將任務(wù)放入阻塞隊列中等待有線程空閑瞒窒。當(dāng)阻塞隊列空間被用完,仍然沒有空閑線程時乡洼,如果線程池中的線程數(shù)量小于等于maximumPoolSize崇裁,則創(chuàng)建新線程處理任務(wù)匕坯,這時如果出現(xiàn)了空閑線程,并且空閑時間大于keepAliveTime寇壳,則此線程會被回收醒颖。當(dāng)線程池的線程數(shù)量達到maximumPoolSize,并且阻塞隊列被填滿壳炎,如果此時有新任務(wù)需要處理泞歉,新任務(wù)會被reject
線程池阻塞隊列
等待被處理的任務(wù)將被暫存在阻塞隊列中,任務(wù)需要實現(xiàn)Runnable接口匿辩,隊列需要實現(xiàn)BlockingQueue接口腰耙。jdk提供了以下幾種阻塞隊列實現(xiàn):
- ArrayBlockingQueue 基于數(shù)組的阻塞隊列,元素按FIFO原則進出隊列
- DelayQueue 創(chuàng)建于堆上的基于優(yōu)先級的無界阻塞隊列铲球,隊頭是最先達到delay時間的元素
- LinkedBlockingQueue 基于鏈表的阻塞隊列挺庞,元素按FIFO原則進出隊列
- LinkedTransferQueue 基于鏈表的阻塞隊列,生產(chǎn)者將阻塞等待消息者讀取元素
- PriorityBlockingQueue 創(chuàng)建于堆上的帶有優(yōu)先級的無界阻塞隊列
- SynchronousQueue 插入操作會被阻塞稼病,直到收到別的線程取走元素的響應(yīng)
任務(wù)reject策略
當(dāng)線程池中的線程數(shù)已達到最大允許線程數(shù)选侨,并且沒有空閑線程,阻塞隊列也已填滿然走,這時再有新的任務(wù)到達到時援制,新任務(wù)會被拒絕。jdk定義了RejectedExecutionHandler接口芍瑞,并提供了幾個不同的實現(xiàn)以提供多種任務(wù)丟棄策略晨仑。
- AbortPolicy 丟棄并拋出異常
- CallerRunsPolicy 直接在調(diào)用方線程中執(zhí)行,如果executor已被關(guān)閉則會被直接丟棄
- DiscardOldestPolicy 將阻塞隊列中最老的任務(wù)丟棄拆檬,然后再次發(fā)起處理請求
- DiscardPolicy 直接將任務(wù)丟棄
Java線程池采用的默認策略是AbortPolicy洪己,丟棄任務(wù)并拋異常,如果需要特別的處理策略,可以自己實現(xiàn)接口RejectedExecutionHandler竟贯。
Java線程池的常見使用
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
創(chuàng)建可以有nThreads個線程的線程池答捕,核心線程數(shù)和最大線程數(shù)都是nThreads,阻塞隊列是基于LinkedBlockingQueue的鏈表阻塞隊列澄耍。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
創(chuàng)建一個線程數(shù)動態(tài)變化的線程池噪珊,被始線程數(shù)0,最大線程數(shù)是Integer.MAX_VALUE齐莲,當(dāng)線程空閑超過60秒會被回收,阻塞隊列是基于SynchronousQueue的磷箕,隊列中并不存儲元素选酗,如果沒有空閑的線程,直接創(chuàng)建新線程處理任務(wù)岳枷。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
創(chuàng)建一個線程池芒填,當(dāng)任務(wù)到達處理時間時會由線程池中的線程處理呜叫,核心線程數(shù)corePoolSize,最大線程數(shù)Integer.MAX_VALUE,當(dāng)線程數(shù)超過核心線程數(shù)后殿衰,一旦線程空閑就會被回收朱庆,阻塞隊列是基于DelayedWorkQueue,delay時間最小的會排在隊頭闷祥。
ThreadPoolExecutor實現(xiàn)
上面幾個線程池的常見用法在底層都是通過ThreadPoolExecutor來實現(xiàn)的娱颊,在ThreadPoolExecutor實現(xiàn)中有一個重要的AtomicInteger類型的變量ctl,它存儲了線程池的兩個重要信息:線程數(shù)量、線程池狀態(tài)
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
COUNT_BITS變量定義了ctl變量中有29位來表示線程數(shù)量凯砍,所以線程池的容量CAPACITY是(2^29)-1個箱硕。ctl中記錄的線程數(shù)量,不僅僅是指處理激活狀態(tài)的可以處理任務(wù)的線程悟衩,如果一個線程準(zhǔn)備被回收剧罩,它已不能處理任務(wù),但是并沒有完成回收座泳,那么這個線程也會被計算在線程數(shù)量之內(nèi)惠昔。
下面介紹一下線程池狀態(tài)
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
ctl變量的高3位表示線程池狀態(tài),共有以下幾種狀態(tài):
- RUNNING 可以接收新任務(wù)挑势,并處理阻塞隊列中的任務(wù)镇防,狀態(tài)碼111
- SHUTDOWN 不接收新任務(wù),但是會處理阻塞隊列中的任務(wù)薛耻,狀態(tài)碼000
- STOP 不接收新任務(wù)营罢,不處理阻塞隊列中的任務(wù),正在處理的任務(wù)會被打斷執(zhí)行饼齿,狀態(tài)001
- TIDYING 所有任務(wù)都已被處理饲漾,線程數(shù)為0,進入此狀態(tài)時會調(diào)用terminated()方法缕溉,狀態(tài)碼010
- TERMINATED terminated()方法執(zhí)行完成后進入此狀態(tài)考传,狀態(tài)碼011
線程池可能的狀態(tài)轉(zhuǎn)換有以下幾種: - RUNNING -> SHUTDOWN 當(dāng)shutdown()方法被調(diào)用的線程池狀態(tài)由RUNNING轉(zhuǎn)換為SHUTDOWN
- (RUNNING/SHUTDOWN) -> STOP 當(dāng)shutdownNow()方法被調(diào)用時,線程池狀態(tài)由RUNNING或SHUTDOWN狀態(tài)轉(zhuǎn)換為STOP
- SHUTDOWN -> TIDYING 當(dāng)線程池處理SHUTDOWN狀態(tài)以后证鸥,如果阻塞隊列已空僚楞,并且線程池中線程數(shù)為0,則線程池轉(zhuǎn)為TIDYING狀態(tài)
- STOP -> TIDYING 當(dāng)線程池處理STOP狀態(tài)枉层,并且線程池里線程數(shù)為0泉褐,則線程池進行TIDYING狀態(tài)
- TIDYING -> TERMINATED 當(dāng)terminated()方法被調(diào)用完成后進入此狀態(tài)
任務(wù)的執(zhí)行
任務(wù)的執(zhí)行是通過調(diào)用execute方法
void execute(Runnable command);
方法實現(xiàn)如下:
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true)) //如果當(dāng)前線程數(shù)小于核心線程數(shù),則創(chuàng)建新線程處理任務(wù)
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) { //如果線程池狀態(tài)是RUNNING鸟蜡,并且任務(wù)被成功存入阻塞隊列
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command)) //再次檢查線程池狀態(tài)膜赃,如果已不是RUNNING,則將任務(wù)由阻塞隊列中移除揉忘,并將任務(wù)reject
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false)) //如果線程池狀態(tài)已不是RUNNING或者任務(wù)放入隊列失敗跳座,則創(chuàng)建新線程處理任務(wù)端铛,如果失敗則reject任務(wù)
reject(command);