前言
線程是稀缺資源碍遍,如果被無限制的創(chuàng)建定铜,不僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性怕敬,合理的使用線程池對線程進行統(tǒng)一分配揣炕、調優(yōu)和監(jiān)控,有以下好處:
- 降低資源消耗东跪;
- 提高響應速度畸陡;
- 提高線程的可管理性
線程池實現(xiàn)原理
如上圖所示,線程池的執(zhí)行分為4步
- workerCountOf()根據(jù)ctl的低29位(ctl高三位是線程池狀態(tài)位越庇,其余29位是線程池中的線程數(shù)量)罩锐,得到線程池的當前線程數(shù),如果線程數(shù)小于corePoolSize卤唉,則執(zhí)行addWorker方法創(chuàng)建新的線程執(zhí)行任務涩惑;否則執(zhí)行步驟2;(執(zhí)行這一步要獲取全局鎖)
- 線程池判斷工作隊列是否已滿桑驱。如果沒有滿則將新任務放入工作隊列竭恬。如果已滿,則進入步驟3熬的;
- 線程池判斷當前線程數(shù)是否大于maximumPoolSize痊硕,如果線程數(shù)小于maximumPoolSize,則創(chuàng)建新線程來處理任務(這一步需要獲取全局鎖)押框;
- 如果線程數(shù)超出maximumPoolSize岔绸,會調用reject方法處理任務;
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 步驟1
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 步驟2
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 步驟3
else if (!addWorker(command, false))
// 步驟4
reject(command);
}
線程池的構造
線程池構造函數(shù)如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
...省略部分源碼
}
- corePoolSize
線程池中的核心線程數(shù)橡伞,當提交一個任務時盒揉,線程池創(chuàng)建一個新線程執(zhí)行任務,直到當前線程數(shù)等于corePoolSize兑徘;如果當前線程數(shù)為corePoolSize刚盈,繼續(xù)提交的任務被保存到阻塞隊列中,等待被執(zhí)行挂脑;如果執(zhí)行了線程池的prestartAllCoreThreads()方法藕漱,線程池會提前創(chuàng)建并啟動所有核心線程欲侮。 - maximumPoolSize
線程池中允許的最大線程數(shù)。如果當前阻塞隊列滿了肋联,且繼續(xù)提交任務威蕉,則創(chuàng)建新的線程執(zhí)行任務,前提是當前線程數(shù)小于maximumPoolSize橄仍; - keepAliveTime
線程空閑時的存活時間忘伞,即當線程沒有任務執(zhí)行時,繼續(xù)存活的時間沙兰;默認情況下,該參數(shù)只在線程數(shù)大于corePoolSize時才有用翘魄; - unit
keepAliveTime的單位鼎天; - workQueue
用來保存等待被執(zhí)行的任務的阻塞隊列,且任務必須實現(xiàn)Runable接口暑竟,在JDK中提供了如下阻塞隊列:
(1)ArrayBlockingQueue:基于數(shù)組結構的有界阻塞隊列斋射,按FIFO排序任務;
(2)LinkedBlockingQuene:基于鏈表結構的阻塞隊列但荤,按FIFO排序任務罗岖,吞吐量通常要高于ArrayBlockingQuene;
(3)SynchronousQuene:一個不存儲元素的阻塞隊列腹躁,每個插入操作必須等到另一個線程調用移除操作桑包,否則插入操作一直處于阻塞狀態(tài),吞吐量通常要高于LinkedBlockingQuene纺非;
(4)priorityBlockingQuene:具有優(yōu)先級的無界阻塞隊列哑了; - threadFactory
創(chuàng)建線程的工廠,通過自定義的線程工廠可以給每個新建的線程設置一個具有識別度的線程名烧颖。 - handler
線程池的飽和策略弱左,當阻塞隊列滿了,且沒有空閑的工作線程炕淮,如果繼續(xù)提交任務拆火,必須采取一種策略處理該任務,線程池提供了4種策略:
(1)AbortPolicy:直接拋出異常涂圆,默認策略们镜;
(2)CallerRunsPolicy:用調用者所在的線程來執(zhí)行任務;
(3)DiscardOldestPolicy:丟棄阻塞隊列中靠最前的任務乘综,并執(zhí)行當前任務憎账;
(4)DiscardPolicy:直接丟棄任務;
當然也可以根據(jù)應用場景實現(xiàn)RejectedExecutionHandler接口卡辰,自定義飽和策略胞皱,如記錄日志或持久化存儲不能處理的任務邪意。
關閉線程池
通過調用shutdown或shutdownNow方法來關閉線程池。它們的原理是遍歷線程池中的工作線程反砌,逐個調用interrupt方法中斷線程雾鬼。它們的區(qū)別是 shutdownNow首先將線程池狀態(tài)設為STOP,然后嘗試停止所有正在執(zhí)行或暫停的工作線程宴树,并返回等待執(zhí)行任務的列表策菜,而shutdown只是將線程池狀態(tài)設為SHUTDOWN,然后中斷沒有執(zhí)行任務的工作線程酒贬。
只要調用其中一個又憨,isShutDown方法會返回true。當所有的線程都已關閉锭吨,才表示線程池關閉成功蠢莺,這是調用isTerminaed方法才會返回true宝泵。