餐廳的約會(huì)
餐盤在燈光的照耀下格外晶瑩潔白,女朋友拿起紅酒杯輕輕地抿了一小口,對(duì)我說:“經(jīng)常聽你說線程池,到底線程池到底是個(gè)什么原理?”我楞了一下,心里想女朋友今天是怎么了,怎么突然問出這么專業(yè)的問題,但做為一個(gè)專業(yè)人士在女朋友面前也不能露怯啊,想了一下便說:“我先給你講講我前同事老王的故事吧!”
歡迎關(guān)注微信公眾號(hào):萬貓學(xué)社,每周一分享Java技術(shù)干貨。
大齡程序員老王
老王是一個(gè)已經(jīng)北漂十多年的程序員,歲數(shù)大了,加班加不動(dòng)了,升遷也無望,于是拿著手里的一些積蓄,回老家轉(zhuǎn)行創(chuàng)業(yè)。他選擇了洗浴行業(yè),開一家洗浴中心,是的,一家正規(guī)的洗浴中心。之前在北京的時(shí)候,喜歡去的澡堂叫“清華池”,他想了想,就給自己的洗浴中心取名為“線程池”。
歡迎關(guān)注微信公眾號(hào):萬貓學(xué)社,每周一分享Java技術(shù)干貨。
線程池洗浴中心
線程池開業(yè)以后,老王發(fā)現(xiàn)有顧客想做足療,于是就招聘了1個(gè)足療技師,多增加了一項(xiàng)業(yè)務(wù)增加了收入。隨著做足療的顧客增多,為了賺更多錢又招聘了4個(gè)足療技師。
過了一段時(shí)間,洗浴中心的生意越來越好,做足療的顧客也越來越多。但是,老王發(fā)現(xiàn)自己店里的足療技師已經(jīng)有5個(gè)足療技師,再招聘就太多了,支付不起再多工資了。足療技師忙不過來怎么辦?老王是個(gè)聰明人,馬上想到辦法:讓顧客排隊(duì),有哪個(gè)足療技師做完了,空閑出來了,就在隊(duì)伍里再叫一個(gè)顧客繼續(xù)做。
歡迎關(guān)注微信公眾號(hào):萬貓學(xué)社,每周一分享Java技術(shù)干貨。
忙碌的周末
一到周末,來洗浴中心的顧客比平時(shí)多了幾倍,想足療的顧客排隊(duì)時(shí)間太長,顧客們已經(jīng)不耐煩了。老王馬上做出反應(yīng),又緊急從其他洗浴中心招聘了5個(gè)足療技師,為隊(duì)伍里顧客做足療,大大減少排隊(duì)的顧客。
不過,有時(shí)生意太火爆了,緊急招聘的技師也用上了,顧客排隊(duì)時(shí)間也是很長,再來新的顧客,老王只能滿臉賠笑地和顧客說:“您下次再來吧,下次給您找個(gè)好技師?!保杨櫩途苤T外。
過了周末以后,店里不能養(yǎng)閑人啊,老王就把緊急招聘的技師都辭退了。歡迎關(guān)注微信公眾號(hào):萬貓學(xué)社,每周一分享Java技術(shù)干貨。
老王的經(jīng)營之道
老王的生意越做越紅火,很快就要開分店、融資上市、走上人生巔峰。既然這么成功,就讓我們來復(fù)盤一下他的經(jīng)營之道吧。
如果你了解了老王的經(jīng)營之道,線程池就不難理解了,把顧客替換成任務(wù),把足療技師替換成線程,線程池洗浴中心就是線程池了,線程池的內(nèi)部原理就是這樣的:
歡迎關(guān)注微信公眾號(hào):萬貓學(xué)社,每周一分享Java技術(shù)干貨。
夢(mèng)醒
鈴鈴鈴,鬧鈴把我吵醒,原來是一場夢(mèng)啊,我哪有什么女朋友?今天上午有一個(gè)面試,趕緊起床洗漱完畢,就出發(fā)了。在路上回想那個(gè)奇怪的夢(mèng),要不再復(fù)習(xí)一下線程池的內(nèi)部原理吧!
先看一下ThreadPoolExecutor類的execute方法:
public void execute(Runnable command) { if (command == null) throw new
NullPointerException(); //獲取clt,clt記錄著線程池狀態(tài)和運(yùn)行線程數(shù)。 int c = ctl.get();
//運(yùn)行線程數(shù)小于核心線程數(shù)時(shí),創(chuàng)建線程放入線程池中,并且運(yùn)行當(dāng)前任務(wù)。 if (workerCountOf(c) < corePoolSize) { if
(addWorker(command, true)) return; //創(chuàng)建線程失敗,重新獲取clt。 c = ctl.get(); }
//線程池是運(yùn)行狀態(tài)并且運(yùn)行線程大于核心線程數(shù)時(shí),把任務(wù)放入隊(duì)列中。 if (isRunning(c) &&
workQueue.offer(command)) { int recheck = ctl.get(); //重新檢查線程池不是運(yùn)行狀態(tài)時(shí),
//把任務(wù)移除隊(duì)列,并通過拒絕策略對(duì)該任務(wù)進(jìn)行處理。 if (! isRunning(recheck) && remove(command))
reject(command); //當(dāng)前運(yùn)行線程數(shù)為0時(shí),創(chuàng)建線程加入線程池中。 else if (workerCountOf(recheck) == 0)
addWorker(null, false); } //運(yùn)行線程大于核心線程數(shù)時(shí)并且隊(duì)列已滿時(shí), //創(chuàng)建線程放入線程池中,并且運(yùn)行當(dāng)前任務(wù)。 else if
(!addWorker(command, false)) //運(yùn)行線程大于最大線程數(shù)時(shí),失敗則拒絕該任務(wù) reject(command); }
在execute方法中,多次調(diào)用的addWorker方法,再看一下這個(gè)方法:
private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;)
{ //獲取clt,clt記錄著線程池狀態(tài)和運(yùn)行線程數(shù)。 int c = ctl.get(); //獲取線程池的運(yùn)行狀態(tài)。 int rs =
runStateOf(c); //線程池處于關(guān)閉狀態(tài),或者當(dāng)前任務(wù)為null //或者隊(duì)列不為空,則直接返回失敗。 if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false;
for (;;) { //獲取線程池中的線程數(shù) int wc = workerCountOf(c); //線程數(shù)超過CAPACITY,則返回false;
//這里的core是addWorker方法的第二個(gè)參數(shù), //如果為true則根據(jù)核心線程數(shù)進(jìn)行比較, //如果為false則根據(jù)最大線程數(shù)進(jìn)行比較。 if
(wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false;
//嘗試增加線程數(shù),如果成功,則跳出第一個(gè)for循環(huán) if (compareAndIncrementWorkerCount(c)) break retry;
//如果增加線程數(shù)失敗,則重新獲取ctl c = ctl.get(); //如果當(dāng)前的運(yùn)行狀態(tài)不等于rs,說明狀態(tài)已被改變, //返回第一個(gè)for循環(huán)繼續(xù)執(zhí)行
if (runStateOf(c) != rs) continue retry; } } boolean workerStarted = false;
boolean workerAdded = false; Worker w = null; try { //根據(jù)當(dāng)前任務(wù)來創(chuàng)建Worker對(duì)象 w = new
Worker(firstTask); final Thread t = w.thread; if (t != null) { final
ReentrantLock mainLock = this.mainLock; mainLock.lock(); try {
//獲得鎖以后,重新檢查線程池狀態(tài) int rs = runStateOf(ctl.get()); if (rs < SHUTDOWN || (rs ==
SHUTDOWN && firstTask == null)) { if (t.isAlive()) throw new
IllegalThreadStateException(); //把剛剛創(chuàng)建的線程加入到線程池中 workers.add(w); int s =
workers.size(); //記錄線程池中出現(xiàn)過的最大線程數(shù)量 if (s > largestPoolSize) largestPoolSize =
s; workerAdded = true; } } finally { mainLock.unlock(); } if (workerAdded) {
//啟動(dòng)線程,開始運(yùn)行任務(wù) t.start(); workerStarted = true; } } } finally { if (!
workerStarted) addWorkerFailed(w); } return workerStarted; }
歡迎關(guān)注微信公眾號(hào):萬貓學(xué)社,每周一分享Java技術(shù)干貨。
面試
一個(gè)中年男子坐在我面前,對(duì)我說:“您好,我是今天的面試官?!蔽椅⑿Φ鼗貞?yīng):“您好?!泵嬖嚬倜鏌o表情地問我:“線程池一定用過吧,能說說線程池的內(nèi)部原理嘛?”我差點(diǎn)笑出聲來,自信滿滿地說……
歡迎關(guān)注微信公眾號(hào):萬貓學(xué)社,每周一分享Java技術(shù)干貨。
熱門工具 換一換