成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

線程池運(yùn)行模型源碼全解析

MockingBird / 3090人閱讀

摘要:那么線程池到底是怎么利用類來實(shí)現(xiàn)持續(xù)不斷地接收提交的任務(wù)并執(zhí)行的呢接下來,我們通過的源代碼來一步一步抽絲剝繭,揭開線程池運(yùn)行模型的神秘面紗。

在上一篇文章《從0到1玩轉(zhuǎn)線程池》中,我們了解了線程池的使用方法,以及向線程池中提交任務(wù)的完整流程和ThreadPoolExecutor.execute方法的源代碼。在這篇文章中,我們將會(huì)從頭閱讀線程池ThreadPoolExecutor類的源代碼,深入剖析線程池從提交任務(wù)到執(zhí)行任務(wù)的完整流程,從而建立起完整的線程池運(yùn)行模型。

查看JDK源碼的方式

在IDE中,例如IDEA里,我們可以點(diǎn)擊我們樣例代碼里的ThreadPoolExecutor類跳轉(zhuǎn)到JDK中ThreadPoolExecutor類的源代碼。在源代碼中我們可以看到很多java.util.concurrent包的締造者大牛“Doug Lea”所留下的各種注釋,下面的圖片就是該類源代碼的一個(gè)截圖。

這些注釋的內(nèi)容非常有參考價(jià)值,建議有能力的讀者朋友可以自己閱讀一遍。下面,我們就開始閱讀ThreadPoolExecutor的源代碼吧。

控制變量與線程池生命周期

ThreadPoolExecutor類定義的開頭,我們可以看到如下的幾行代碼:

// 控制變量,前3位表示狀態(tài),剩下的數(shù)據(jù)位表示有效的線程數(shù)
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// Integer的位數(shù)減去3位狀態(tài)位就是線程數(shù)的位數(shù)
private static final int COUNT_BITS = Integer.SIZE - 3;
// CAPACITY就是線程數(shù)的上限(含),即2^COUNT_BITS - 1個(gè)
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

第一行是一個(gè)用來作為控制變量的整型值,即一個(gè)Integer。之所以要用AtomicInteger類是因?yàn)橐WC多線程安全,在本系列之后的文章中會(huì)對AtomicInteger進(jìn)行具體介紹。一個(gè)整型一般是32位,但是這里的代碼為了保險(xiǎn)起見,還是使用了Integer.SIZE來表示整型的總位數(shù)。這里的“位”指的是數(shù)據(jù)位(bit),在計(jì)算機(jī)中,8bit = 1字節(jié),1024字節(jié) = 1KB,1024KB = 1MB。每一位都是一個(gè)0或1的數(shù)字,我們?nèi)绻颜拖胂蟪梢粋€(gè)二進(jìn)制(0或1)的數(shù)組,那么一個(gè)Integer就是32個(gè)數(shù)字的數(shù)組。其中,前三個(gè)被用來表示狀態(tài),那么我們就可以表示2^3 = 8個(gè)不同的狀態(tài)了。剩下的29位二進(jìn)制數(shù)字都會(huì)被用于表示當(dāng)前線程池中有效線程的數(shù)量,上限就是(2^29 - 1)個(gè),即常量CAPACITY

之后的部分列出了線程池的所有狀態(tài):

private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

在這里可以忽略數(shù)字后面的<< COUNT_BITS,可以把狀態(tài)簡單地理解為前面的數(shù)字部分,這樣的簡化基本不影響結(jié)論。

各個(gè)狀態(tài)的解釋如下:

RUNNING,正常運(yùn)行狀態(tài),可以接受新的任務(wù)和處理隊(duì)列中的任務(wù)

SHUTDOWN,關(guān)閉中狀態(tài),不能接受新任務(wù),但是可以處理隊(duì)列中的任務(wù)

STOP,停止中狀態(tài),不能接受新任務(wù),也不處理隊(duì)列中的任務(wù),會(huì)中斷進(jìn)行中的任務(wù)

TIDYING,待結(jié)束狀態(tài),所有任務(wù)已經(jīng)結(jié)束,線程數(shù)歸0,進(jìn)入TIDYING狀態(tài)后將會(huì)運(yùn)行terminated()方法

TERMINATED,結(jié)束狀態(tài),terminated()方法調(diào)用完成后進(jìn)入

這幾個(gè)狀態(tài)所對應(yīng)的數(shù)字值是按照順序排列的,也就是說線程池的狀態(tài)只能從小到大變化,這也方便了通過數(shù)字比較來判斷狀態(tài)所在的階段,這種通過數(shù)字大小來比較狀態(tài)值的方法在ThreadPoolExecutor的源碼中會(huì)有大量的使用。

下圖是這五個(gè)狀態(tài)之間的變化過程:

當(dāng)線程池被創(chuàng)建時(shí)會(huì)處于RUNNING狀態(tài),正常接受和處理任務(wù);

當(dāng)shutdown()方法被直接調(diào)用,或者在線程池對象被GC回收時(shí)通過finalize()方法隱式調(diào)用了shutdown()方法時(shí),線程池會(huì)進(jìn)入SHUTDOWN狀態(tài)。該狀態(tài)下線程池仍然會(huì)繼續(xù)執(zhí)行完阻塞隊(duì)列中的任務(wù),只是不再接受新的任務(wù)了。當(dāng)隊(duì)列中的任務(wù)被執(zhí)行完后,線程池中的線程也會(huì)被回收。當(dāng)隊(duì)列和線程都被清空后,線程池將進(jìn)入TIDYING狀態(tài);

在線程池處于RUNNING或者SHUTDOWN狀態(tài)時(shí),如果有代碼調(diào)用了shutdownNow()方法,則線程池會(huì)進(jìn)入STOP狀態(tài)。在STOP狀態(tài)下,線程池會(huì)直接清空阻塞隊(duì)列中待執(zhí)行的任務(wù),然后中斷所有正在進(jìn)行中的任務(wù)并回收線程。當(dāng)線程都被清空以后,線程池就會(huì)進(jìn)入TIDYING狀態(tài);

當(dāng)線程池進(jìn)入TIDYING狀態(tài)時(shí),將會(huì)運(yùn)行terminated()方法,該方法執(zhí)行完后,線程池就會(huì)進(jìn)入最終的TERMINATED狀態(tài),徹底結(jié)束。

到這里我們就已經(jīng)清楚地了解了線程從剛被創(chuàng)建時(shí)的RUNNING狀態(tài)一直到最終的TERMINATED狀態(tài)的整個(gè)生命周期了。那么當(dāng)我們要向一個(gè)RUNNING狀態(tài)的線程池提交任務(wù)時(shí)會(huì)發(fā)生些什么呢?

execute方法的實(shí)現(xiàn)

我們一般會(huì)使用execute方法提交我們的任務(wù),那么線程池在這個(gè)過程中做了什么呢?在ThreadPoolExecutor類的execute()方法的源代碼中,我們主要做了四件事:

如果當(dāng)前線程池中的線程數(shù)小于核心線程數(shù)corePoolSize,則通過threadFactory創(chuàng)建一個(gè)新的線程,并把入?yún)⒅械娜蝿?wù)作為第一個(gè)任務(wù)傳入該線程;

如果當(dāng)前線程池中的線程數(shù)已經(jīng)達(dá)到了核心線程數(shù)corePoolSize,那么就會(huì)通過阻塞隊(duì)列workerQueueoffer方法來將任務(wù)添加到隊(duì)列中保存,并等待線程空閑后進(jìn)行執(zhí)行;

如果線程數(shù)已經(jīng)達(dá)到了corePoolSize且阻塞隊(duì)列中無法插入該任務(wù)(比如已滿),那么線程池就會(huì)再增加一個(gè)線程來執(zhí)行該任務(wù),除非線程數(shù)已經(jīng)達(dá)到了最大線程數(shù)maximumPoolSize

如果確實(shí)已經(jīng)達(dá)到了最大線程數(shù),那么就會(huì)通過拒絕策略對象handler拒絕這個(gè)任務(wù)。

總體上的執(zhí)行流程如下,下方的黑色同心圓代表流程結(jié)束:

這里解釋一下阻塞隊(duì)列的定義,方便大家閱讀:

線程池中的阻塞隊(duì)列專門用于存放需要等待線程空閑的待執(zhí)行任務(wù),而阻塞隊(duì)列是這樣的一種數(shù)據(jù)結(jié)構(gòu),它是一個(gè)隊(duì)列(類似于一個(gè)List),可以存放0到N個(gè)元素。我們可以對這個(gè)隊(duì)列進(jìn)行插入和彈出元素的操作,彈出操作可以理解為是一個(gè)獲取并從隊(duì)列中刪除一個(gè)元素的操作。當(dāng)隊(duì)列中沒有元素時(shí),對這個(gè)隊(duì)列的獲取操作將會(huì)被阻塞,直到有元素被插入時(shí)才會(huì)被喚醒;當(dāng)隊(duì)列已滿時(shí),對這個(gè)隊(duì)列的插入操作將會(huì)被阻塞,直到有元素被彈出后才會(huì)被喚醒。

這樣的一種數(shù)據(jù)結(jié)構(gòu)非常適合于線程池的場景,當(dāng)一個(gè)工作線程沒有任務(wù)可處理時(shí)就會(huì)進(jìn)入阻塞狀態(tài),直到有新任務(wù)提交后才被喚醒。

線程池中常用的阻塞隊(duì)列一般有三種類型:直連隊(duì)列、無界隊(duì)列、有界隊(duì)列。不同的阻塞隊(duì)列類型會(huì)被線程池的行為產(chǎn)生不同的影響,有興趣的讀者可以在上一篇文章《從0到1玩轉(zhuǎn)線程池》中找到不同類型阻塞隊(duì)列的具體解釋。

下面是帶有注釋的源代碼,大家可以和上面的流程對照起來參考一下:

public void execute(Runnable command) {
    // 檢查提交的任務(wù)是否為空
    if (command == null)
        throw new NullPointerException();
    
    // 獲取控制變量值
    int c = ctl.get();
    // 檢查當(dāng)前線程數(shù)是否達(dá)到了核心線程數(shù)
    if (workerCountOf(c) < corePoolSize) {
        // 未達(dá)到核心線程數(shù),則創(chuàng)建新線程
        // 并將傳入的任務(wù)作為該線程的第一個(gè)任務(wù)
        if (addWorker(command, true))
            // 添加線程成功則直接返回,否則繼續(xù)執(zhí)行
            return;

        // 因?yàn)榍懊嬲{(diào)用了耗時(shí)操作addWorker方法
        // 所以線程池狀態(tài)有可能發(fā)生了改變,重新獲取狀態(tài)值
        c = ctl.get();
    }

    // 判斷線程池當(dāng)前狀態(tài)是否是運(yùn)行中
    // 如果是則調(diào)用workQueue.offer方法將任務(wù)放入阻塞隊(duì)列
    if (isRunning(c) && workQueue.offer(command)) {
        // 因?yàn)閳?zhí)行了耗時(shí)操作“放入阻塞隊(duì)列”,所以重新獲取狀態(tài)值
        int recheck = ctl.get();
        // 如果當(dāng)前狀態(tài)不是運(yùn)行中,則將剛才放入阻塞隊(duì)列的任務(wù)拿出,如果拿出成功,則直接拒絕這個(gè)任務(wù)
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            // 如果線程池中沒有線程了,那就創(chuàng)建一個(gè)
            addWorker(null, false);
    }
    // 如果放入阻塞隊(duì)列失?。ㄈ珀?duì)列已滿),則添加一個(gè)線程
    else if (!addWorker(command, false))
        // 如果添加線程失?。ㄈ缫呀?jīng)達(dá)到了最大線程數(shù)),則拒絕任務(wù)
        reject(command);
}

從上面的源碼中我們可以知道,當(dāng)一個(gè)任務(wù)被通過ThreadPoolExecutorexecute方法提交到線程池中執(zhí)行時(shí),這個(gè)任務(wù)有可能以兩種方式被執(zhí)行:

直接在創(chuàng)建一個(gè)新的Worker時(shí)被作為第一個(gè)任務(wù)傳入,由這個(gè)新創(chuàng)建的線程來執(zhí)行;

把任務(wù)放入一個(gè)阻塞隊(duì)列,等待線程池中的工作線程Worker撈取任務(wù)進(jìn)行執(zhí)行。

這里的這個(gè)Worker指的就是ThreadPoolExecutor.Worker類,這是一個(gè)ThreadPoolExecutor的內(nèi)部類,用于對基礎(chǔ)線程類Thread進(jìn)行包裝和對線程進(jìn)行管理。那么線程池到底是怎么利用Worker類來實(shí)現(xiàn)持續(xù)不斷地接收提交的任務(wù)并執(zhí)行的呢?接下來,我們通過ThreadPoolExecutor的源代碼來一步一步抽絲剝繭,揭開線程池運(yùn)行模型的神秘面紗。

addWorker方法

在上文中的execute方法的代碼中我們可以看到線程池是通過addWorker方法來向線程池中添加新線程的,那么新的線程又是如何運(yùn)行起來的呢?

這里我們暫時(shí)跳過addWorker方法的詳細(xì)源代碼,因?yàn)殡m然這個(gè)方法的代碼行數(shù)較多,但是功能相對比較直接,只是通過new Worker(firstTask)創(chuàng)建了一個(gè)代表線程的Worker對象,然后調(diào)用了這個(gè)對象所包含的Thread對象的start()方法。

我們知道一旦調(diào)用了Thread類的start()方法,則這個(gè)線程就會(huì)開始執(zhí)行創(chuàng)建線程時(shí)傳入的Runnable對象。從下面的Worker類構(gòu)造器源代碼可以看出,Worker類正是把自己(this引用)傳入了線程的構(gòu)造器當(dāng)中,所以這個(gè)線程啟動(dòng)后就會(huì)執(zhí)行Worker類的run()方法了,而在Workerrun()方法中只執(zhí)行了一行很簡單的代碼runWorker(this)。

Worker(Runnable firstTask) {
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask;
    this.thread = getThreadFactory().newThread(this);
}

public void run() {
    runWorker(this);
}
runWorker方法的實(shí)現(xiàn)

我們看到線程池中的線程在啟動(dòng)時(shí)會(huì)調(diào)用對應(yīng)的Worker類的runWorker方法,而這里就是整個(gè)線程池任務(wù)執(zhí)行的核心所在了。runWorker方法中包含有一個(gè)類似無限循環(huán)的while語句,讓worker對象可以一直持續(xù)不斷地執(zhí)行提交到線程池中的新任務(wù)或者等待下一個(gè)新任務(wù)的提交。

大家可以配合代碼上帶有的注釋來理解該方法的具體實(shí)現(xiàn):

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    // 將worker的狀態(tài)重置為正常狀態(tài),因?yàn)閟tate狀態(tài)值在構(gòu)造器中被初始化為-1
    w.unlock();
    // 通過completedAbruptly變量的值判斷任務(wù)是否正常執(zhí)行完成
    boolean completedAbruptly = true;
    try {
        // 如果task為null就通過getTask方法獲取阻塞隊(duì)列中的下一個(gè)任務(wù)
        // getTask方法一般不會(huì)返回null,所以這個(gè)while類似于一個(gè)無限循環(huán)
        // worker對象就通過這個(gè)方法的持續(xù)運(yùn)行來不斷處理新的任務(wù)
        while (task != null || (task = getTask()) != null) {
            // 每一次任務(wù)的執(zhí)行都必須獲取鎖來保證下方臨界區(qū)代碼的線程安全
            w.lock();
            
            // 如果狀態(tài)值大于等于STOP(狀態(tài)值是有序的,即STOP、TIDYING、TERMINATED)
            // 且當(dāng)前線程還沒有被中斷,則主動(dòng)中斷線程
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();

            // 開始
            try {
                // 執(zhí)行任務(wù)前處理操作,默認(rèn)是一個(gè)空實(shí)現(xiàn)
                // 在子類中可以通過重寫來改變?nèi)蝿?wù)執(zhí)行前的處理行為
                beforeExecute(wt, task);

                // 通過thrown變量保存任務(wù)執(zhí)行過程中拋出的異常
                // 提供給下面finally塊中的afterExecute方法使用
                Throwable thrown = null;
                try {
                    // *** 重要:實(shí)際執(zhí)行任務(wù)的代碼
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    // 因?yàn)镽unnable接口的run方法中不能拋出Throwable對象
                    // 所以要包裝成Error對象拋出
                    thrown = x; throw new Error(x);
                } finally {
                    // 執(zhí)行任務(wù)后處理操作,默認(rèn)是一個(gè)空實(shí)現(xiàn)
                    // 在子類中可以通過重寫來改變?nèi)蝿?wù)執(zhí)行后的處理行為
                    afterExecute(task, thrown);
                }
            } finally {
                // 將循環(huán)變量task設(shè)置為null,表示已處理完成
                task = null;
                // 累加當(dāng)前worker已經(jīng)完成的任務(wù)數(shù)
                w.completedTasks++;
                // 釋放while體中第一行獲取的鎖
                w.unlock();
            }
        }

        // 將completedAbruptly變量設(shè)置為false,表示任務(wù)正常處理完成
        completedAbruptly = false;
    } finally {
        // 銷毀當(dāng)前的worker對象,并完成一些諸如完成任務(wù)數(shù)量統(tǒng)計(jì)之類的輔助性工作
        // 在線程池當(dāng)前狀態(tài)小于STOP的情況下會(huì)創(chuàng)建一個(gè)新的worker來替換被銷毀的worker
        processWorkerExit(w, completedAbruptly);
    }
}

runWorker方法的源代碼中有兩個(gè)比較重要的方法調(diào)用,一個(gè)是while條件中對getTask方法的調(diào)用,一個(gè)是在方法的最后對processWorkerExit方法的調(diào)用。下面是對這兩個(gè)方法更詳細(xì)的解釋。

getTask方法在阻塞隊(duì)列中有待執(zhí)行的任務(wù)時(shí)會(huì)從隊(duì)列中彈出一個(gè)任務(wù)并返回,如果阻塞隊(duì)列為空,那么就會(huì)阻塞等待新的任務(wù)提交到隊(duì)列中直到超時(shí)(在一些配置下會(huì)一直等待而不超時(shí)),如果在超時(shí)之前獲取到了新的任務(wù),那么就會(huì)將這個(gè)任務(wù)作為返回值返回。所以一般getTask方法是不會(huì)返回null的,只會(huì)阻塞等待下一個(gè)任務(wù)并在之后將這個(gè)新任務(wù)作為返回值返回。

當(dāng)getTask方法返回null時(shí)會(huì)導(dǎo)致當(dāng)前Worker退出,當(dāng)前線程被銷毀。在以下情況下getTask方法才會(huì)返回null:

當(dāng)前線程池中的線程數(shù)超過了最大線程數(shù)。這是因?yàn)檫\(yùn)行時(shí)通過調(diào)用setMaximumPoolSize修改了最大線程數(shù)而導(dǎo)致的結(jié)果;

線程池處于STOP狀態(tài)。這種情況下所有線程都應(yīng)該被立即回收銷毀;

線程池處于SHUTDOWN狀態(tài),且阻塞隊(duì)列為空。這種情況下已經(jīng)不會(huì)有新的任務(wù)被提交到阻塞隊(duì)列中了,所以線程應(yīng)該被銷毀;

線程可以被超時(shí)回收的情況下等待新任務(wù)超時(shí)。線程被超時(shí)回收一般有以下兩種情況:

超出核心線程數(shù)部分的線程等待任務(wù)超時(shí)

允許核心線程超時(shí)(線程池配置)的情況下線程等待任務(wù)超時(shí)

processWorkerExit方法會(huì)銷毀當(dāng)前線程對應(yīng)的Worker對象,并執(zhí)行一些累加總處理任務(wù)數(shù)等輔助操作,但在線程池當(dāng)前狀態(tài)小于STOP的情況下會(huì)創(chuàng)建一個(gè)新的Worker來替換被銷毀的Worker。

getTaskprocessWorkerExit方法源代碼感興趣的讀者可以閱讀下一節(jié)來具體了解一下,不過跳過這一節(jié)也是完全可以的。

getTask與processWorkerExit方法源代碼

以下是getTaskprocessWorkerExit兩個(gè)方法的帶有中文解釋的源代碼:

private Runnable getTask() {
    // 通過timeOut變量表示線程是否空閑時(shí)間超時(shí)了
    boolean timedOut = false;

    // 無限循環(huán)
    for (;;) {
        // 獲取線程池狀態(tài)
        int c = ctl.get();
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        // 如果 線程池狀態(tài)>=STOP
        //    或者 (線程池狀態(tài)==SHUTDOWN && 阻塞隊(duì)列為空)
        // 則直接減少一個(gè)worker計(jì)數(shù)并返回null(返回null會(huì)導(dǎo)致當(dāng)前worker被銷毀)
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        // 獲取線程池中的worker計(jì)數(shù)
        int wc = workerCountOf(c);

        // 判斷當(dāng)前線程是否會(huì)被超時(shí)銷毀
        // 會(huì)被超時(shí)銷毀的情況:線程池允許核心線程超時(shí) 或 當(dāng)前線程數(shù)大于核心線程數(shù)
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        // 如果 (當(dāng)前線程數(shù)大于最大線程數(shù) 或 (允許超時(shí)銷毀 且 當(dāng)前發(fā)生了空閑時(shí)間超時(shí)))
        //   且 (當(dāng)前線程數(shù)大于1 或 阻塞隊(duì)列為空) —— 該條件在阻塞隊(duì)列不為空的情況下保證至少會(huì)保留一個(gè)線程繼續(xù)處理任務(wù)
        // 則 減少worker計(jì)數(shù)并返回null(返回null會(huì)導(dǎo)致當(dāng)前worker被銷毀)
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            // 從阻塞隊(duì)列中取出一個(gè)任務(wù)(如果隊(duì)列為空會(huì)進(jìn)入阻塞等待狀態(tài))
            // 如果允許空閑超時(shí)銷毀線程的話則帶有一個(gè)等待的超時(shí)時(shí)間
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            // 如果獲取到了任務(wù)就直接返回該任務(wù),返回后會(huì)開始執(zhí)行該任務(wù)
            if (r != null)
                return r;
            // 如果任務(wù)為null,則說明發(fā)生了等待超時(shí),將空閑時(shí)間超時(shí)標(biāo)志設(shè)置為true
            timedOut = true;
        } catch (InterruptedException retry) {
            // 如果等待被中斷了,那說明空閑時(shí)間(等待任務(wù)的時(shí)間)還沒有超時(shí)
            timedOut = false;
        }
    }
}

processWorkerExit方法的源代碼:

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    // 如果completedAbruptly為true則表示任務(wù)執(zhí)行過程中拋出了未處理的異常
    // 所以還沒有正確地減少worker計(jì)數(shù),這里需要減少一次worker計(jì)數(shù)
    if (completedAbruptly)
        decrementWorkerCount();

    // 獲取線程池的主鎖
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 把將被銷毀的線程已完成的任務(wù)數(shù)累計(jì)到線程池的完成任務(wù)總數(shù)上
        completedTaskCount += w.completedTasks;
        // 從worker集合中去掉將會(huì)銷毀的worker
        workers.remove(w);
    } finally {
        // 釋放線程池主鎖
        mainLock.unlock();
    }

    // 嘗試結(jié)束線程池
    // 這里是為了在關(guān)閉線程池時(shí)等到所有worker都被回收后再結(jié)束線程池
    tryTerminate();

    int c = ctl.get();
    // 如果線程池狀態(tài) < STOP,即RUNNING或SHUTDOWN
    // 則需要考慮創(chuàng)建新線程來代替被銷毀的線程
    if (runStateLessThan(c, STOP)) {
        // 如果worker是正常執(zhí)行完的,則要判斷一下是否已經(jīng)滿足了最小線程數(shù)要求
        // 否則直接創(chuàng)建替代線程
        if (!completedAbruptly) {
            // 如果允許核心線程超時(shí)則最小線程數(shù)是0,否則最小線程數(shù)等于核心線程數(shù)
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            // 如果阻塞隊(duì)列非空,則至少要有一個(gè)線程繼續(xù)執(zhí)行剩下的任務(wù)
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            // 如果當(dāng)前線程數(shù)已經(jīng)滿足最小線程數(shù)要求
            // 那么就不創(chuàng)建替代線程了
            if (workerCountOf(c) >= min)
                return;
        }

        // 重新創(chuàng)建一個(gè)worker來代替被銷毀的線程
        addWorker(null, false);
    }
}
總結(jié)

到這里我們的線程池源代碼之旅就結(jié)束了,在這篇文章中我們首先了解了線程池中的控制變量與狀態(tài)變換流程,之后我們通過線程池的源代碼深入解析了從提交任務(wù)到執(zhí)行任務(wù)的全過程,相信通過這些知識我們已經(jīng)可以在腦海中建立起一套完整的線程池運(yùn)行模型了。如果大家有一些細(xì)節(jié)感覺還不是特別清晰的話,建議不妨再返回到文章的開頭多讀幾遍,相信第二遍的閱讀能給大家?guī)聿灰粯拥捏w驗(yàn),因?yàn)槲易约阂彩窃诘谌巫xThreadPoolExecutor類的源代碼時(shí)才真正打通了其中的一些重要關(guān)節(jié)的。

引子

在瀏覽ThreadPoolExexutor源碼的過程中,有幾個(gè)點(diǎn)我們其實(shí)并沒有完全說清楚,比如對鎖的加鎖操作、對控制變量的多次獲取、控制變量的AtomicInteger類型。在下一篇文章中,我將會(huì)介紹這些以鎖、volatile變量、CAS操作、AQS抽象類為代表的一系列線程同步方法,歡迎感興趣的讀者繼續(xù)關(guān)注我后續(xù)發(fā)布的文章~

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/73889.html

相關(guān)文章

  • 線程編程完指南

    摘要:在這個(gè)范圍廣大的并發(fā)技術(shù)領(lǐng)域當(dāng)中多線程編程可以說是基礎(chǔ)和核心,大多數(shù)抽象并發(fā)問題的構(gòu)思與解決都是基于多線程模型來進(jìn)行的。一般來說,多線程程序會(huì)面臨三類問題正確性問題效率問題死鎖問題。 多線程編程或者說范圍更大的并發(fā)編程是一種非常復(fù)雜且容易出錯(cuò)的編程方式,但是我們?yōu)槭裁催€要冒著風(fēng)險(xiǎn)艱辛地學(xué)習(xí)各種多線程編程技術(shù)、解決各種并發(fā)問題呢? 因?yàn)椴l(fā)是整個(gè)分布式集群的基礎(chǔ),通過分布式集群不僅可以大...

    mengera88 評論0 收藏0
  • java篇

    摘要:多線程編程這篇文章分析了多線程的優(yōu)缺點(diǎn),如何創(chuàng)建多線程,分享了線程安全和線程通信線程池等等一些知識。 中間件技術(shù)入門教程 中間件技術(shù)入門教程,本博客介紹了 ESB、MQ、JMS 的一些知識... SpringBoot 多數(shù)據(jù)源 SpringBoot 使用主從數(shù)據(jù)源 簡易的后臺管理權(quán)限設(shè)計(jì) 從零開始搭建自己權(quán)限管理框架 Docker 多步構(gòu)建更小的 Java 鏡像 Docker Jav...

    honhon 評論0 收藏0
  • 讀書筆記之深入理解Java虛擬機(jī)

    摘要:前言本文內(nèi)容基本摘抄自深入理解虛擬機(jī),以供復(fù)習(xí)之用,沒有多少參考價(jià)值。此區(qū)域是唯一一個(gè)在虛擬機(jī)規(guī)范中沒有規(guī)定任何情況的區(qū)域。堆是所有線程共享的內(nèi)存區(qū)域,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。虛擬機(jī)上把方法區(qū)稱為永久代。 前言 本文內(nèi)容基本摘抄自《深入理解Java虛擬機(jī)》,以供復(fù)習(xí)之用,沒有多少參考價(jià)值。想要更詳細(xì)了解請參考原書。 第二章 1.運(yùn)行時(shí)數(shù)據(jù)區(qū)域 showImg(https://segment...

    jaysun 評論0 收藏0
  • 面向?qū)ο蟮姆植际脚老x框架XXL-CRAWLER

    摘要:面向?qū)ο蟮姆植际脚老x框架一簡介概述是一個(gè)面向?qū)ο蟮姆植际脚老x框架。分布式集群集群方式維護(hù)爬蟲爬蟲運(yùn)行數(shù)據(jù),可通過或定制實(shí)現(xiàn)。 《面向?qū)ο蟮姆植际脚老x框架XXL-CRAWLER》 showImg(https://segmentfault.com/img/remote/1460000011842697);showImg(https://segmentfault.com/img/remote...

    anquan 評論0 收藏0
  • 為Java程序員金三銀四精心挑選的300余道Java面試題與答案

    摘要:為程序員金三銀四精心挑選的余道面試題與答案,歡迎大家向我推薦你在面試過程中遇到的問題我會(huì)把大家推薦的問題添加到下面的常用面試題清單中供大家參考。 為Java程序員金三銀四精心挑選的300余道Java面試題與答案,歡迎大家向我推薦你在面試過程中遇到的問題,我會(huì)把大家推薦的問題添加到下面的常用面試題清單中供大家參考。 前兩天寫的以下博客,大家比較認(rèn)可,熱度不錯(cuò),希望可以幫到準(zhǔn)備或者正在參加...

    tomorrowwu 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<