多線程--精通ThreadPoolExecutor

前言

在多線程開發(fā)中,應(yīng)該避免顯式創(chuàng)建線程,而是采用線程池里面的線程谭企。使用線程池可以減少手動(dòng)創(chuàng)建線程,減少線程創(chuàng)建和回收的損耗等评肆。那么使用線程池就需要了解它的原理债查。這里我們ThreadPoolExecutor.execute()方法內(nèi)部的具體實(shí)現(xiàn)邏輯

流程圖

image.png

源碼分析

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
       // 獲取狀態(tài)變量
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
      // 如果worker個(gè)數(shù)小于核心線程數(shù)量
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 嘗試將任務(wù)丟入工作隊(duì)列中
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
      // 工作隊(duì)列滿了的話再次嘗試增加worker
        else if (!addWorker(command, false))
            reject(command);
    }
  private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                 // 獲取worker數(shù)量
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    // 判斷是否大于核心線程數(shù)量或者超過線程池總大小
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            // 創(chuàng)建新的worker
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // 單線程處理工作者入隊(duì)
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // 這時(shí)候的worker不能已經(jīng)是在運(yùn)行中的
                            throw new IllegalThreadStateException();
                      // 將worker加入到工作者的集合中
                        workers.add(w);
                        int s = workers.size();
                      //   更新線程池的當(dāng)前最大size
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    //   如果加入到工作者集合中成功,那么就啟動(dòng)工作者
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }
// 這個(gè)方法是工作者的實(shí)際工作內(nèi)容瓜挽,看下工作者是怎么處理任務(wù)的
  final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // 允許中斷
        boolean completedAbruptly = true;
        try {
            // 如果工作者攜帶的任務(wù)或任務(wù)隊(duì)列不是空的盹廷,就會(huì)一直循環(huán)
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        // 處理任務(wù)執(zhí)行之后的邏輯
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            // 處理worker退出的邏輯
            processWorkerExit(w, completedAbruptly);
        }
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市久橙,隨后出現(xiàn)的幾起案子俄占,更是在濱河造成了極大的恐慌,老刑警劉巖淆衷,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缸榄,死亡現(xiàn)場離奇詭異,居然都是意外死亡祝拯,警方通過查閱死者的電腦和手機(jī)甚带,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來佳头,“玉大人鹰贵,你說我怎么就攤上這事】导危” “怎么了砾莱?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長凄鼻。 經(jīng)常有香客問我腊瑟,道長,這世上最難降的妖魔是什么块蚌? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任闰非,我火速辦了婚禮,結(jié)果婚禮上峭范,老公的妹妹穿的比我還像新娘财松。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布辆毡。 她就那樣靜靜地躺著菜秦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪舶掖。 梳的紋絲不亂的頭發(fā)上球昨,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天,我揣著相機(jī)與錄音眨攘,去河邊找鬼主慰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛鲫售,可吹牛的內(nèi)容都是我干的共螺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼情竹,長吁一口氣:“原來是場噩夢啊……” “哼藐不!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起秦效,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤佳吞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后棉安,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體底扳,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年贡耽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了衷模。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,488評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蒲赂,死狀恐怖阱冶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情滥嘴,我是刑警寧澤木蹬,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站若皱,受9級特大地震影響镊叁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜走触,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一晦譬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧互广,春花似錦敛腌、人聲如沸卧土。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尤莺。三九已至,卻和暖如春生棍,著一層夾襖步出監(jiān)牢的瞬間颤霎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工足绅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人韩脑。 一個(gè)月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓氢妈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親段多。 傳聞我的和親對象是個(gè)殘疾皇子首量,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容