通俗易懂地給女朋友講:執行緒池的內部原理

萬貓學社發表於2020-01-11

在這裡插入圖片描述

餐廳的約會

餐盤在燈光的照耀下格外晶瑩潔白,女朋友拿起紅酒杯輕輕地抿了一小口,對我說:“經常聽你說執行緒池,到底執行緒池到底是個什麼原理?”我楞了一下,心裡想女朋友今天是怎麼了,怎麼突然問出這麼專業的問題,但做為一個專業人士在女朋友面前也不能露怯啊,想了一下便說:“我先給你講講我前同事老王的故事吧!”

大齡程式設計師老王

老王是一個已經北漂十多年的程式設計師,歲數大了,加班加不動了,升遷也無望,於是拿著手裡的一些積蓄,回老家轉行創業。他選擇了洗浴行業,開一家洗浴中心,是的,一家正規的洗浴中心。之前在北京的時候,喜歡去的澡堂叫“清華池”,他想了想,就給自己的洗浴中心取名為“執行緒池”。

執行緒池洗浴中心

執行緒池開業以後,老王發現有顧客想做足療,於是就招聘了1個足療技師,多增加了一項業務增加了收入。隨著做足療的顧客增多,為了賺更多錢又招聘了4個足療技師。
過了一段時間,洗浴中心的生意越來越好,做足療的顧客也越來越多。但是,老王發現自己店裡的足療技師已經有5個足療技師,再招聘就太多了,支付不起再多工資了。足療技師忙不過來怎麼辦?老王是個聰明人,馬上想到辦法:讓顧客排隊,有哪個足療技師做完了,空閒出來了,就在隊伍裡再叫一個顧客繼續做。

忙碌的週末

一到週末,來洗浴中心的顧客比平時多了幾倍,想足療的顧客排隊時間太長,顧客們已經不耐煩了。老王馬上做出反應,又緊急從其他洗浴中心招聘了5個足療技師,為隊伍裡顧客做足療,大大減少排隊的顧客。
不過,有時生意太火爆了,緊急招聘的技師也用上了,顧客排隊時間也是很長,再來新的顧客,老王只能滿臉賠笑地和顧客說:“您下次再來吧,下次給您找個好技師。”,把顧客拒之門外。
過了週末以後,店裡不能養閒人啊,老王就把緊急招聘的技師都辭退了。

老王的經營之道

老王的生意越做越紅火,很快就要開分店、融資上市、走上人生巔峰。既然這麼成功,就讓我們來複盤一下他的經營之道吧。
在這裡插入圖片描述
如果你瞭解了老王的經營之道,執行緒池就不難理解了,把顧客替換成任務,把足療技師替換成執行緒執行緒池洗浴中心就是執行緒池了,執行緒池的內部原理就是這樣的:
在這裡插入圖片描述

夢醒

鈴鈴鈴,鬧鈴把我吵醒,原來是一場夢啊,我哪有什麼女朋友?今天上午有一個面試,趕緊起床洗漱完畢,就出發了。在路上回想那個奇怪的夢,要不再複習一下執行緒池的內部原理吧!
先看一下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方法,再看一下這個方法:

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;
}

面試

一箇中年男子坐在我面前,對我說:“您好,我是今天的面試官。”我微笑地迴應:“您好。”面試官面無表情地問我:“執行緒池一定用過吧,能說說執行緒池的內部原理嘛?”我差點笑出聲來,自信滿滿地說……


熱文推薦:

相關文章