Java小白也能聽懂的線程池的內部原理:老王的洗浴中心

餐廳的約會

餐盤在燈光的照耀下格外晶瑩潔白,女友拿起紅酒杯輕輕地抿了一小口,對我說:「常常聽你說線程池,到底線程池究竟是個什麼原理?」我楞了一下,內心想女友今天是怎麼了,怎麼忽然問出這麼專業的問題,但作爲一個專業人士在女友面前也不能露怯啊,想了一下便說:「我先給你講講我前同事老王的故事吧!」程序員

大齡程序員老王

老王是一個已經北漂十多年的程序員,歲數大了,加班加不動了,升遷也無望,因而拿着手裏的一些積蓄,回老家轉行創業。他選擇了洗浴行業,開一家洗浴中心,是的,一家正規的洗浴中心。以前在北京的時候,喜歡去的澡堂叫「清華池」,他想了想,就給本身的洗浴中心取名爲「線程池」。面試

線程池洗浴中心

線程池開業之後,老王發現有顧客想作足療,因而就招聘了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

相關文章
相關標籤/搜索