餐盤在燈光的照耀下格外晶瑩潔白,女友拿起紅酒杯輕輕地抿了一小口,對我說:「常常聽你說線程池,到底線程池究竟是個什麼原理?」我楞了一下,內心想女友今天是怎麼了,怎麼忽然問出這麼專業的問題,但作爲一個專業人士在女友面前也不能露怯啊,想了一下便說:「我先給你講講我前同事老王的故事吧!」程序員
老王是一個已經北漂十多年的程序員,歲數大了,加班加不動了,升遷也無望,因而拿着手裏的一些積蓄,回老家轉行創業。他選擇了洗浴行業,開一家洗浴中心,是的,一家正規的洗浴中心。以前在北京的時候,喜歡去的澡堂叫「清華池」,他想了想,就給本身的洗浴中心取名爲「線程池」。面試
線程池開業之後,老王發現有顧客想作足療,因而就招聘了1個足療技師,多增長了一項業務增長了收入。隨着作足療的顧客增多,爲了賺更多錢又招聘了4個足療技師。 過了一段時間,洗浴中心的生意愈來愈好,作足療的顧客也愈來愈多。可是,老王發現本身店裏的足療技師已經有5個足療技師,再招聘就太多了,支付不起再多工資了。足療技師忙不過來怎麼辦?老王是個聰明人,立刻想到辦法:讓顧客排隊,有哪一個足療技師作完了,空閒出來了,就在隊伍裏再叫一個顧客繼續作。bash
一到週末,來洗浴中心的顧客比平時多了幾倍,想足療的顧客排隊時間太長,顧客們已經不耐煩了。老王立刻作出反應,又緊急從其餘洗浴中心招聘了5個足療技師,爲隊伍裏顧客作足療,大大減小排隊的顧客。 不過,有時生意太火爆了,緊急招聘的技師也用上了,顧客排隊時間也是很長,再來新的顧客,老王只能滿臉賠笑地和顧客說:「您下次再來吧,下次給您找個好技師。」,把顧客拒之門外。 過了週末之後,店裏不能養閒人啊,老王就把緊急招聘的技師都辭退了。ui
老王的生意越作越紅火,很快就要開分店、融資上市、走上人生巔峯。既然這麼成功,就讓咱們來複盤一下他的經營之道吧。this
若是你瞭解了老王的經營之道,線程池就不難理解了,把顧客替換成任務,把足療技師替換成線程,線程池洗浴中心就是線程池了,線程池的內部原理就是這樣的:spa
鈴鈴鈴,鬧鈴把我吵醒,原來是一場夢啊,我哪有什麼女友?今天上午有一個面試,趕忙起牀洗漱完畢,就出發了。在路上回想那個奇怪的夢,要再也不復習一下線程池的內部原理吧! 先看一下ThreadPoolExecutor類的execute方法:線程
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//獲取clt,clt記錄着線程池狀態和運行線程數。
int c = ctl.get();
//運行線程數小於核心線程數時,建立線程放入線程池中,而且運行當前任務。
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
//建立線程失敗,從新獲取clt。
c = ctl.get();
}
//線程池是運行狀態而且運行線程大於核心線程數時,把任務放入隊列中。
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//從新檢查線程池不是運行狀態時,
//把任務移除隊列,並經過拒絕策略對該任務進行處理。
if (! isRunning(recheck) && remove(command))
reject(command);
//當前運行線程數爲0時,建立線程加入線程池中。
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//運行線程大於核心線程數時而且隊列已滿時,
//建立線程放入線程池中,而且運行當前任務。
else if (!addWorker(command, false))
//運行線程大於最大線程數時,失敗則拒絕該任務
reject(command);
}
複製代碼
在execute方法中,屢次調用的addWorker方法,再看一下這個方法:code
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
//獲取clt,clt記錄着線程池狀態和運行線程數。
int c = ctl.get();
//獲取線程池的運行狀態。
int rs = runStateOf(c);
//線程池處於關閉狀態,或者當前任務爲null
//或者隊列不爲空,則直接返回失敗。
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
//獲取線程池中的線程數
int wc = workerCountOf(c);
//線程數超過CAPACITY,則返回false;
//這裏的core是addWorker方法的第二個參數,
//若是爲true則根據核心線程數進行比較,
//若是爲false則根據最大線程數進行比較。
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//嘗試增長線程數,若是成功,則跳出第一個for循環
if (compareAndIncrementWorkerCount(c))
break retry;
//若是增長線程數失敗,則從新獲取ctl
c = ctl.get();
//若是當前的運行狀態不等於rs,說明狀態已被改變,
//返回第一個for循環繼續執行
if (runStateOf(c) != rs)
continue retry;
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
//根據當前任務來建立Worker對象
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//得到鎖之後,從新檢查線程池狀態
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive())
throw new IllegalThreadStateException();
//把剛剛建立的線程加入到線程池中
workers.add(w);
int s = workers.size();
//記錄線程池中出現過的最大線程數量
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
//啓動線程,開始運行任務
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
複製代碼
一箇中年男子坐在我面前,對我說:「您好,我是今天的面試官。」我微笑地迴應:「您好。」面試官面無表情地問我:「線程池必定用過吧,能說說線程池的內部原理嘛?」我差點笑出聲來,自信滿滿地說……cdn