餐盤在燈光的照耀下格外晶瑩潔白,女友拿起紅酒杯輕輕地抿了一小口,對我說:「常常聽你說線程池,到底線程池究竟是個什麼原理?」我楞了一下,內心想女友今天是怎麼了,怎麼忽然問出這麼專業的問題,但作爲一個專業人士在女友面前也不能露怯啊,想了一下便說:「我先給你講講我前同事老王的故事吧!」歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。java
老王是一個已經北漂十多年的程序員,歲數大了,加班加不動了,升遷也無望,因而拿着手裏的一些積蓄,回老家轉行創業。他選擇了洗浴行業,開一家洗浴中心,是的,一家正規的洗浴中心。以前在北京的時候,喜歡去的澡堂叫「清華池」,他想了想,就給本身的洗浴中心取名爲「線程池」。歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。程序員
線程池開業之後,老王發現有顧客想作足療,因而就招聘了1個足療技師,多增長了一項業務增長了收入。隨着作足療的顧客增多,爲了賺更多錢又招聘了4個足療技師。
過了一段時間,洗浴中心的生意愈來愈好,作足療的顧客也愈來愈多。可是,老王發現本身店裏的足療技師已經有5個足療技師,再招聘就太多了,支付不起再多工資了。足療技師忙不過來怎麼辦?老王是個聰明人,立刻想到辦法:讓顧客排隊,有哪一個足療技師作完了,空閒出來了,就在隊伍裏再叫一個顧客繼續作。歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。面試
一到週末,來洗浴中心的顧客比平時多了幾倍,想足療的顧客排隊時間太長,顧客們已經不耐煩了。老王立刻作出反應,又緊急從其餘洗浴中心招聘了5個足療技師,爲隊伍裏顧客作足療,大大減小排隊的顧客。
不過,有時生意太火爆了,緊急招聘的技師也用上了,顧客排隊時間也是很長,再來新的顧客,老王只能滿臉賠笑地和顧客說:「您下次再來吧,下次給您找個好技師。」,把顧客拒之門外。
過了週末之後,店裏不能養閒人啊,老王就把緊急招聘的技師都辭退了。歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。微信
老王的生意越作越紅火,很快就要開分店、融資上市、走上人生巔峯。既然這麼成功,就讓咱們來複盤一下他的經營之道吧。
若是你瞭解了老王的經營之道,線程池就不難理解了,把顧客
替換成任務
,把足療技師
替換成線程
,線程池洗浴中心
就是線程池
了,線程池的內部原理就是這樣的:
歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。this
鈴鈴鈴,鬧鈴把我吵醒,原來是一場夢啊,我哪有什麼女友?今天上午有一個面試,趕忙起牀洗漱完畢,就出發了。在路上回想那個奇怪的夢,要再也不復習一下線程池的內部原理吧!
先看一下ThreadPoolExecutor類的execute方法:spa
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方法,再看一下這個方法:線程
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; }
歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。3d
一箇中年男子坐在我面前,對我說:「您好,我是今天的面試官。」我微笑地迴應:「您好。」面試官面無表情地問我:「線程池必定用過吧,能說說線程池的內部原理嘛?」我差點笑出聲來,自信滿滿地說……歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。rest