java線程池原理

前言:
線程是稀缺資源咬腋,如果被無(wú)限制的創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源睡互,還會(huì)降低系統(tǒng)的穩(wěn)定性根竿,合理的使用線程池對(duì)線程進(jìn)行統(tǒng)一分配陵像、調(diào)優(yōu)和監(jiān)控,有以下好處:
1寇壳、降低資源消耗醒颖;
2、提高響應(yīng)速度壳炎;
3泞歉、提高線程的可管理性。

線程池用一個(gè)32位的int來(lái)同時(shí)保存runState和workerCount匿辩,其中高3位是runState腰耙,其余29位是workerCount。代碼中會(huì)反復(fù)使用runStateOf和workerCountOf來(lái)獲取runState和workerCount铲球。


2184951-16c59af5d9d66527.png

1挺庞、Executors.newFixedThreadPool(10)初始化一個(gè)包含10個(gè)線程的線程池executor;
2稼病、通過(guò)executor.execute方法提交20個(gè)任務(wù)选侨,每個(gè)任務(wù)打印當(dāng)前的線程名;
3然走、負(fù)責(zé)執(zhí)行任務(wù)的線程的生命周期都由Executor框架進(jìn)行管理援制;

ThreadPoolExecutor
Executors是java線程池的工廠類,通過(guò)它可以快速初始化一個(gè)符合業(yè)務(wù)需求的線程池芍瑞,如Executors.newFixedThreadPool方法可以生成一個(gè)擁有固定線程數(shù)的線程池晨仑。


2184951-35585a83d35ee516.png

其本質(zhì)是通過(guò)不同的參數(shù)初始化一個(gè)ThreadPoolExecutor對(duì)象,具體參數(shù)描述如下:
corePoolSize:
線程池中的核心線程數(shù)拆檬,當(dāng)提交一個(gè)任務(wù)時(shí)寻歧,線程池創(chuàng)建一個(gè)新線程執(zhí)行任務(wù),直到當(dāng)前線程數(shù)等于corePoolSize秩仆;如果當(dāng)前線程數(shù)為corePoolSize,繼續(xù)提交的任務(wù)被保存到阻塞隊(duì)列中猾封,等待被執(zhí)行澄耍;如果執(zhí)行了線程池的prestartAllCoreThreads()方法,線程池會(huì)提前創(chuàng)建并啟動(dòng)所有核心線程晌缘。

maximumPoolSize:
線程池中允許的最大線程數(shù)齐莲。如果當(dāng)前阻塞隊(duì)列滿了,且繼續(xù)提交任務(wù)磷箕,則創(chuàng)建新的線程執(zhí)行任務(wù)选酗,前提是當(dāng)前線程數(shù)小于maximumPoolSize;

keepAliveTime:
線程空閑時(shí)的存活時(shí)間岳枷,即當(dāng)線程沒(méi)有任務(wù)執(zhí)行時(shí)芒填,繼續(xù)存活的時(shí)間呜叫;默認(rèn)情況下,該參數(shù)只在線程數(shù)大于corePoolSize時(shí)才有用殿衰;

unit:
keepAliveTime的單位朱庆;

workQueue:
用來(lái)保存等待被執(zhí)行的任務(wù)的阻塞隊(duì)列,且任務(wù)必須實(shí)現(xiàn)Runable接口闷祥,在JDK中提供了如下阻塞隊(duì)列:
1娱颊、ArrayBlockingQueue:基于數(shù)組結(jié)構(gòu)的有界阻塞隊(duì)列,按FIFO排序任務(wù)凯砍;
2箱硕、LinkedBlockingQuene:基于鏈表結(jié)構(gòu)的阻塞隊(duì)列,按FIFO排序任務(wù)悟衩,吞吐量通常要高于ArrayBlockingQuene剧罩;
3、SynchronousQuene:一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列局待,每個(gè)插入操作必須等到另一個(gè)線程調(diào)用移除操作斑响,否則插入操作一直處于阻塞狀態(tài),吞吐量通常要高于LinkedBlockingQuene钳榨;
4舰罚、priorityBlockingQuene:具有優(yōu)先級(jí)的無(wú)界阻塞隊(duì)列;

threadFactory:
創(chuàng)建線程的工廠,通過(guò)自定義的線程工廠可以給每個(gè)新建的線程設(shè)置一個(gè)具有識(shí)別度的線程名陶耍。


2184951-d2d8fd007c7f7a27.png

handler:
線程池的飽和策略毁涉,當(dāng)阻塞隊(duì)列滿了,且沒(méi)有空閑的工作線程饲漾,如果繼續(xù)提交任務(wù),必須采取一種策略處理該任務(wù)缕溉,線程池提供了4種策略:
1考传、AbortPolicy:直接拋出異常,默認(rèn)策略证鸥;
2僚楞、CallerRunsPolicy:用調(diào)用者所在的線程來(lái)執(zhí)行任務(wù);
3枉层、DiscardOldestPolicy:丟棄阻塞隊(duì)列中靠最前的任務(wù)泉褐,并執(zhí)行當(dāng)前任務(wù);
4鸟蜡、DiscardPolicy:直接丟棄任務(wù)膜赃;
當(dāng)然也可以根據(jù)應(yīng)用場(chǎng)景實(shí)現(xiàn)RejectedExecutionHandler接口,自定義飽和策略揉忘,如記錄日志或持久化存儲(chǔ)不能處理的任務(wù)跳座。

Exectors:
Exectors工廠類提供了線程池的初始化接口端铛,主要有如下幾種:
newFixedThreadPool


2184951-35585a83d35ee516.png

初始化一個(gè)指定線程數(shù)的線程池,其中corePoolSize == maximumPoolSize躺坟,使用LinkedBlockingQuene作為阻塞隊(duì)列沦补,不過(guò)當(dāng)線程池沒(méi)有可執(zhí)行任務(wù)時(shí),也不會(huì)釋放線程咪橙。

newCachedThreadPool


2184951-9b76630ac48f318c.png

1夕膀、初始化一個(gè)可以緩存線程的線程池,默認(rèn)緩存60s美侦,線程池的線程數(shù)可達(dá)到Integer.MAX_VALUE产舞,即2147483647,內(nèi)部使用SynchronousQueue作為阻塞隊(duì)列菠剩;
2易猫、和newFixedThreadPool創(chuàng)建的線程池不同,newCachedThreadPool在沒(méi)有任務(wù)執(zhí)行時(shí)具壮,當(dāng)線程的空閑時(shí)間超過(guò)keepAliveTime准颓,會(huì)自動(dòng)釋放線程資源,當(dāng)提交新任務(wù)時(shí)棺妓,如果沒(méi)有空閑線程攘已,則創(chuàng)建新線程執(zhí)行任務(wù),會(huì)導(dǎo)致一定的系統(tǒng)開銷怜跑;

所以样勃,使用該線程池時(shí),一定要注意控制并發(fā)的任務(wù)數(shù)性芬,否則創(chuàng)建大量的線程可能導(dǎo)致嚴(yán)重的性能問(wèn)題

newSingleThreadExecutor


2184951-deded05302aaf255.png

初始化的線程池中只有一個(gè)線程峡眶,如果該線程異常結(jié)束,會(huì)重新創(chuàng)建一個(gè)新的線程繼續(xù)執(zhí)行任務(wù)植锉,唯一的線程可以保證所提交任務(wù)的順序執(zhí)行辫樱,內(nèi)部使用LinkedBlockingQueue作為阻塞隊(duì)列。

newScheduledThreadPool


2184951-14823f17e4e4a09e.png

初始化的線程池可以在指定的時(shí)間內(nèi)周期性的執(zhí)行所提交的任務(wù)俊庇,在實(shí)際的業(yè)務(wù)場(chǎng)景中可以使用該線程池定期的同步數(shù)據(jù)狮暑。

實(shí)現(xiàn)原理
除了newScheduledThreadPool的內(nèi)部實(shí)現(xiàn)特殊一點(diǎn)之外,其它幾個(gè)線程池都是基于ThreadPoolExecutor類實(shí)現(xiàn)的暇赤。

線程池內(nèi)部狀態(tài)


2184951-5a620e0f56cbb008.png

其中AtomicInteger變量ctl的功能非常強(qiáng)大:利用低29位表示線程池中線程數(shù),通過(guò)高3位表示線程池的運(yùn)行狀態(tài):
1宵凌、RUNNING:-1 << COUNT_BITS鞋囊,即高3位為111,該狀態(tài)的線程池會(huì)接收新任務(wù)瞎惫,并處理阻塞隊(duì)列中的任務(wù)溜腐;
2译株、SHUTDOWN: 0 << COUNT_BITS,即高3位為000挺益,該狀態(tài)的線程池不會(huì)接收新任務(wù)歉糜,但會(huì)處理阻塞隊(duì)列中的任務(wù);
3望众、STOP : 1 << COUNT_BITS匪补,即高3位為001,該狀態(tài)的線程不會(huì)接收新任務(wù)烂翰,也不會(huì)處理阻塞隊(duì)列中的任務(wù)夯缺,而且會(huì)中斷正在運(yùn)行的任務(wù);
4甘耿、TIDYING : 2 << COUNT_BITS踊兜,即高3位為010;
5佳恬、TERMINATED: 3 << COUNT_BITS捏境,即高3位為011;

線程池狀態(tài)默認(rèn)從RUNNING開始流轉(zhuǎn)毁葱,到狀態(tài)TERMINATED結(jié)束垫言,中間不需要經(jīng)過(guò)每一種狀態(tài),但不能讓狀態(tài)回退头谜。下面是狀態(tài)變化可能的路徑和變化條件:

3294095-6a507005d933355b.png

任務(wù)提交
線程池框架提供了兩種方式提交任務(wù)骏掀,根據(jù)不同的業(yè)務(wù)需求選擇不同的方式。

Executor.execute()


2184951-834971c24d085d31.png

通過(guò)Executor.execute()方法提交的任務(wù)柱告,必須實(shí)現(xiàn)Runnable接口截驮,該方式提交的任務(wù)不能獲取返回值,因此無(wú)法判斷任務(wù)是否執(zhí)行成功际度。

ExecutorService.submit()


2184951-ea9fe289ca3de89f.png

通過(guò)ExecutorService.submit()方法提交的任務(wù)葵袭,可以獲取任務(wù)執(zhí)行完的返回值。

任務(wù)執(zhí)行
當(dāng)向線程池中提交一個(gè)任務(wù)乖菱,線程池會(huì)如何處理該任務(wù)坡锡?

execute實(shí)現(xiàn):
調(diào)用execute將會(huì)根據(jù)線程池的情況創(chuàng)建Worker,可以歸納出下圖四種情況:


3294095-c3b458db36a11d5e.png

標(biāo)記1對(duì)應(yīng)第一種情況窒所,要留意addWorker傳入了core鹉勒,core=true為corePoolSize,core=false為maximumPoolSize吵取,新增時(shí)需要檢查workerCount是否超過(guò)允許的最大值禽额。
標(biāo)記2對(duì)應(yīng)第二種情況,檢查線程池是否在運(yùn)行,并且將任務(wù)加入等待隊(duì)列脯倒。標(biāo)記3再檢查一次線程池狀態(tài)实辑,如果線程池忽然處于非運(yùn)行狀態(tài),那就將等待隊(duì)列剛加的任務(wù)刪掉藻丢,再交給RejectedExecutionHandler處理剪撬。標(biāo)記4發(fā)現(xiàn)沒(méi)有worker,就先補(bǔ)充一個(gè)空任務(wù)的worker悠反。
標(biāo)記5對(duì)應(yīng)第三種情況残黑,等待隊(duì)列不能再添加任務(wù)了,調(diào)用addWorker添加一個(gè)去處理问慎。
標(biāo)記6對(duì)應(yīng)第四種情況萍摊,addWorker的core傳入false,返回調(diào)用失敗如叼,代表workerCount已經(jīng)超出maximumPoolSize冰木,那就交給RejectedExecutionHandler處理。

addWorker實(shí)現(xiàn)
從方法execute的實(shí)現(xiàn)可以看出:addWorker主要負(fù)責(zé)創(chuàng)建新的線程并執(zhí)行任務(wù)笼恰,代碼實(shí)現(xiàn)如下:

private boolean addWorker(Runnable firstTask, boolean core) {
        //1
        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 (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    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
            }
        }
        //2
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            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;
    }

在addWoker的標(biāo)記1部分:
1踊沸、判斷線程池的狀態(tài),如果線程池的狀態(tài)值大于或等SHUTDOWN社证,則不處理提交的任務(wù)逼龟,直接返回;
2追葡、通過(guò)參數(shù)core判斷當(dāng)前需要?jiǎng)?chuàng)建的線程是否為核心線程腺律,如果core為true,且當(dāng)前線程數(shù)小于corePoolSize宜肉,則跳出循環(huán)匀钧,開始創(chuàng)建新的線程,具體實(shí)現(xiàn)在標(biāo)記2部分谬返。

在標(biāo)記2部分中之斯,線程池的工作線程通過(guò)Woker類實(shí)現(xiàn),在ReentrantLock鎖的保證下遣铝,把Woker實(shí)例插入到HashSet后佑刷,并啟動(dòng)Woker中的線程,其中Worker類設(shè)計(jì)如下:
1酿炸、繼承了AQS類瘫絮,可以方便的實(shí)現(xiàn)工作線程的中止操作;
2填硕、實(shí)現(xiàn)了Runnable接口麦萤,可以將自身作為一個(gè)任務(wù)在工作線程中執(zhí)行;
3、當(dāng)前提交的任務(wù)firstTask作為參數(shù)傳入Worker的構(gòu)造方法频鉴;

Worker的執(zhí)行

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

public void run() {
    runWorker(this);
}

從Woker類的構(gòu)造方法實(shí)現(xiàn)可以發(fā)現(xiàn):線程工廠在創(chuàng)建線程thread時(shí),將Woker實(shí)例本身this作為參數(shù)傳入恋拍,當(dāng)執(zhí)行start方法啟動(dòng)線程thread時(shí)垛孔,本質(zhì)是執(zhí)行了Worker的runWorker方法。

runWorker實(shí)現(xiàn):


2184951-1e8ed00138c189ea.png

runWorker方法是線程池的核心:
1施敢、線程啟動(dòng)之后周荐,通過(guò)unlock方法釋放鎖,設(shè)置AQS的state為0僵娃,表示運(yùn)行中斷概作;
2、獲取第一個(gè)任務(wù)firstTask默怨,執(zhí)行任務(wù)的run方法讯榕,不過(guò)在執(zhí)行任務(wù)之前,會(huì)進(jìn)行加鎖操作匙睹,任務(wù)執(zhí)行完會(huì)釋放鎖愚屁;
3、在執(zhí)行任務(wù)的前后痕檬,可以根據(jù)業(yè)務(wù)場(chǎng)景自定義beforeExecute和afterExecute方法霎槐;
4、firstTask執(zhí)行完成之后梦谜,通過(guò)getTask方法從阻塞隊(duì)列中獲取等待的任務(wù)丘跌,如果隊(duì)列中沒(méi)有任務(wù),getTask方法會(huì)被阻塞并掛起唁桩,不會(huì)占用cpu資源闭树;

getTask實(shí)現(xiàn):


2184951-a63a6646c456f715.png

整個(gè)getTask操作在自旋下完成:
1、workQueue.take:如果阻塞隊(duì)列為空朵夏,當(dāng)前線程會(huì)被掛起等待蔼啦;當(dāng)隊(duì)列中有任務(wù)加入時(shí),線程被喚醒仰猖,take方法返回任務(wù)捏肢,并執(zhí)行;
2饥侵、workQueue.poll:如果在keepAliveTime時(shí)間內(nèi)鸵赫,阻塞隊(duì)列還是沒(méi)有任務(wù),則返回null躏升;

所以辩棒,線程池中實(shí)現(xiàn)的線程可以一直執(zhí)行由用戶提交的任務(wù)。

Future和Callable實(shí)現(xiàn)
通過(guò)ExecutorService.submit()方法提交的任務(wù),可以獲取任務(wù)執(zhí)行完的返回值一睁。


2184951-cdacf86769a61288.png

在實(shí)際業(yè)務(wù)場(chǎng)景中钻弄,F(xiàn)uture和Callable基本是成對(duì)出現(xiàn)的,Callable負(fù)責(zé)產(chǎn)生結(jié)果者吁,F(xiàn)uture負(fù)責(zé)獲取結(jié)果窘俺。
1、Callable接口類似于Runnable复凳,只是Runnable沒(méi)有返回值瘤泪。
2、Callable任務(wù)除了返回正常結(jié)果之外育八,如果發(fā)生異常对途,該異常也會(huì)被返回,即Future可以拿到異步執(zhí)行任務(wù)各種結(jié)果髓棋;
3实檀、Future.get方法會(huì)導(dǎo)致主線程阻塞,直到Callable任務(wù)執(zhí)行完成按声;

submit實(shí)現(xiàn):


2184951-15281a586352e509.png

通過(guò)submit方法提交的Callable任務(wù)會(huì)被封裝成了一個(gè)FutureTask對(duì)象劲妙。

FutureTask:


2184951-d109631c519443cc.png

1、FutureTask在不同階段擁有不同的狀態(tài)state儒喊,初始化為NEW镣奋;
2、FutureTask類實(shí)現(xiàn)了Runnable接口怀愧,這樣就可以通過(guò)Executor.execute方法提交FutureTask到線程池中等待被執(zhí)行侨颈,最終執(zhí)行的是FutureTask的run方法;

FutureTask.get實(shí)現(xiàn):


2184951-62fdfdd3f3a14e22.png

內(nèi)部通過(guò)awaitDone方法對(duì)主線程進(jìn)行阻塞芯义,具體實(shí)現(xiàn)如下:


2184951-aa841730137097ac.png

1哈垢、如果主線程被中斷,則拋出中斷異常扛拨;
2耘分、判斷FutureTask當(dāng)前的state,如果大于COMPLETING绑警,說(shuō)明任務(wù)已經(jīng)執(zhí)行完成求泰,則直接返回;
3计盒、如果當(dāng)前state等于COMPLETING渴频,說(shuō)明任務(wù)已經(jīng)執(zhí)行完,這時(shí)主線程只需通過(guò)yield方法讓出cpu資源北启,等待state變成NORMAL卜朗;
4拔第、通過(guò)WaitNode類封裝當(dāng)前線程,并通過(guò)UNSAFE添加到waiters鏈表场钉;
5蚊俺、最終通過(guò)LockSupport的park或parkNanos掛起線程;

FutureTask.run實(shí)現(xiàn):


2184951-29642db6d9c6589e.png

FutureTask.run方法是在線程池中被執(zhí)行的逛万,而非主線程
1春叫、通過(guò)執(zhí)行Callable任務(wù)的call方法;
2泣港、如果call執(zhí)行成功,則通過(guò)set方法保存結(jié)果价匠;
3当纱、如果call執(zhí)行有異常,則通過(guò)setException保存異常踩窖;

set:


2184951-19a5160726da146d.png

setException:


2184951-34a8bba028ba58be.png

set和setException方法中坡氯,都會(huì)通過(guò)UnSAFE修改FutureTask的狀態(tài),并執(zhí)行finishCompletion方法通知主線程任務(wù)已經(jīng)執(zhí)行完成洋腮;

finishCompletion:


2184951-c380e58cdcb02e31.png

1箫柳、執(zhí)行FutureTask類的get方法時(shí),會(huì)把主線程封裝成WaitNode節(jié)點(diǎn)并保存在waiters鏈表中啥供;
2悯恍、FutureTask任務(wù)執(zhí)行完成后,通過(guò)UNSAFE設(shè)置waiters的值伙狐,并通過(guò)LockSupport類unpark方法喚醒主線程涮毫;

最后來(lái)看結(jié)束worker需要執(zhí)行的操作:

private void processWorkerExit(Worker w, boolean completedAbruptly) {
   //1
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        decrementWorkerCount();

  //2
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        completedTaskCount += w.completedTasks;
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }

   //3
    tryTerminate();

    int c = ctl.get();
    //4
    if (runStateLessThan(c, STOP)) {
        if (!completedAbruptly) {
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        addWorker(null, false);
    }
}

正常情況下,在getTask里就會(huì)將workerCount減一贷屎。標(biāo)記1處用變量completedAbruptly判斷worker是否異常退出罢防,如果是,需要補(bǔ)充對(duì)workerCount的減一唉侄。
標(biāo)記2將worker處理任務(wù)的數(shù)量累加到總數(shù)咒吐,并且在集合workers中去除。
標(biāo)記3嘗試終止線程池属划,后續(xù)會(huì)研究恬叹。
標(biāo)記4處理線程池還是RUNNING或SHUTDOWN狀態(tài)時(shí),如果worker是異常結(jié)束同眯,那么會(huì)直接addWorker妄呕。如果allowCoreThreadTimeOut=true,并且等待隊(duì)列有任務(wù)嗽测,至少保留一個(gè)worker绪励;如果allowCoreThreadTimeOut=false肿孵,workerCount不少于corePoolSize。

線程池的關(guān)閉
線程池的關(guān)閉不是一關(guān)了事疏魏,worker在池里處于不同狀態(tài)停做,必須安排好worker的"后事",才能真正釋放線程池大莫。ThreadPoolExecutor提供兩種方法關(guān)閉線程池:

shutdown:不能再提交任務(wù)蛉腌,已經(jīng)提交的任務(wù)可繼續(xù)運(yùn)行;
shutdownNow:不能再提交任務(wù)只厘,已經(jīng)提交但未執(zhí)行的任務(wù)不能運(yùn)行烙丛,在運(yùn)行的任務(wù)可繼續(xù)運(yùn)行,但會(huì)被中斷羔味,返回已經(jīng)提交但未執(zhí)行的任務(wù)河咽。

public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();   //1 安全策略機(jī)制
        advanceRunState(SHUTDOWN);   //2
        interruptIdleWorkers();   //3
        onShutdown(); //4 空方法,子類實(shí)現(xiàn)
    } finally {
        mainLock.unlock();
    }
    tryTerminate();   //5
}

shutdown將線程池切換到SHUTDOWN狀態(tài)赋元,并調(diào)用interruptIdleWorkers請(qǐng)求中斷所有空閑的worker忘蟹,最后調(diào)用tryTerminate嘗試結(jié)束線程池。

public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(STOP);
        interruptWorkers();
        tasks = drainQueue();  //1
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}

shutdownNow和shutdown類似搁凸,將線程池切換為STOP狀態(tài)媚值,中斷目標(biāo)是所有worker。drainQueue會(huì)將等待隊(duì)列里未執(zhí)行的任務(wù)返回护糖。

interruptIdleWorkers和interruptWorkers實(shí)現(xiàn)原理都是遍歷workers集合褥芒,中斷條件符合的worker。

上面的代碼多次出現(xiàn)調(diào)用tryTerminate嫡良,這是一個(gè)嘗試將線程池切換到TERMINATED狀態(tài)的方法喂很。

final void tryTerminate() {
    for (;;) {
        int c = ctl.get();
        //1
        if (isRunning(c) ||
            runStateAtLeast(c, TIDYING) ||
            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
            return;
        //2
        if (workerCountOf(c) != 0) { // Eligible to terminate
            interruptIdleWorkers(ONLY_ONE);
            return;
        }
       //3
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                try {
                    terminated();
                } finally {
                    ctl.set(ctlOf(TERMINATED, 0));
                    termination.signalAll();
                }
                return;
            }
        } finally {
            mainLock.unlock();
        }
        // else retry on failed CAS
    }
}

標(biāo)記1檢查線程池狀態(tài),下面幾種情況皆刺,后續(xù)操作都沒(méi)有必要少辣,直接return。

RUNNING(還在運(yùn)行羡蛾,不能停)
TIDYING或TERMINATED(已經(jīng)沒(méi)有在運(yùn)行的worker)
SHUTDOWN并且等待隊(duì)列非空(執(zhí)行完才能停)
標(biāo)記2在worker非空的情況下又調(diào)用了interruptIdleWorkers漓帅,你可能疑惑在shutdown時(shí)已經(jīng)調(diào)用過(guò)了,為什么又調(diào)用痴怨,而且每次只中斷一個(gè)空閑worker忙干?你需要知道,shutdown時(shí)worker可能在執(zhí)行中浪藻,執(zhí)行完阻塞在隊(duì)列的take捐迫,不知道要結(jié)束,所有要補(bǔ)充調(diào)用interruptIdleWorkers爱葵。每次只中斷一個(gè)是因?yàn)閜rocessWorkerExit時(shí)施戴,還會(huì)執(zhí)行tryTerminate反浓,自動(dòng)中斷下一個(gè)空閑的worker。

標(biāo)記3是最終的狀態(tài)切換赞哗。線程池會(huì)先進(jìn)入TIDYING狀態(tài)雷则,再進(jìn)入TERMINATED狀態(tài),中間提供了terminated這個(gè)空方法供子類實(shí)現(xiàn)肪笋。

調(diào)用關(guān)閉線程池方法后月劈,需要等待線程池切換到TERMINATED狀態(tài)。awaitTermination檢查限定時(shí)間內(nèi)線程池是否進(jìn)入TERMINATED狀態(tài)藤乙,代碼如下:

public boolean awaitTermination(long timeout, TimeUnit unit)
    throws InterruptedException {
    long nanos = unit.toNanos(timeout);
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (;;) {
            if (runStateAtLeast(ctl.get(), TERMINATED))
                return true;
            if (nanos <= 0)
                return false;
            nanos = termination.awaitNanos(nanos);
        }
    } finally {
        mainLock.unlock();
    }
}

參考文章:
http://www.reibang.com/p/87bff5cc8d8c
http://www.reibang.com/p/f62a3f452869

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末猜揪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子坛梁,更是在濱河造成了極大的恐慌而姐,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罚勾,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡吭狡,警方通過(guò)查閱死者的電腦和手機(jī)尖殃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)划煮,“玉大人送丰,你說(shuō)我怎么就攤上這事〕谇铮” “怎么了器躏?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蟹略。 經(jīng)常有香客問(wèn)我登失,道長(zhǎng),這世上最難降的妖魔是什么挖炬? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任揽浙,我火速辦了婚禮,結(jié)果婚禮上意敛,老公的妹妹穿的比我還像新娘馅巷。我一直安慰自己,他們只是感情好草姻,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布钓猬。 她就那樣靜靜地躺著,像睡著了一般撩独。 火紅的嫁衣襯著肌膚如雪敞曹。 梳的紋絲不亂的頭發(fā)上账月,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音异雁,去河邊找鬼捶障。 笑死,一個(gè)胖子當(dāng)著我的面吹牛纲刀,可吹牛的內(nèi)容都是我干的项炼。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼示绊,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼锭部!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起面褐,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拌禾,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后展哭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體湃窍,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年匪傍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了您市。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡役衡,死狀恐怖茵休,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情手蝎,我是刑警寧澤榕莺,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站棵介,受9級(jí)特大地震影響钉鸯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜邮辽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一亏拉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧逆巍,春花似錦及塘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至灵再,卻和暖如春肋层,著一層夾襖步出監(jiān)牢的瞬間亿笤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工栋猖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留净薛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓蒲拉,卻偏偏與公主長(zhǎng)得像肃拜,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子雌团,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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

  • 前言 使用線程池能夠提高線程的復(fù)用率燃领,避免不必要的創(chuàng)建線程,能夠節(jié)約內(nèi)存空間和CPU運(yùn)行時(shí)間锦援。除此之外用線程池作為...
    Mars_M閱讀 2,842評(píng)論 0 11
  • 一猛蔽、線程池工廠Executors 我們平時(shí)在使用線程池的時(shí)候一般都是通過(guò)Executors的newXxxxxPoo...
    丑星星閱讀 498評(píng)論 0 1
  • 前言 在上一篇線程池的文章《Java線程池原理分析ThreadPoolExecutor篇》中從ThreadPool...
    Mars_M閱讀 2,537評(píng)論 0 4
  • 合理使用線程池能夠帶來(lái)3個(gè)好處。 第一灵寺,降低資源消耗曼库。通過(guò)重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。第二略板,...
    薛晨閱讀 6,167評(píng)論 3 9
  • 傷害你更深的毁枯,往生是那些口頭上的兄弟。 01 大個(gè)是我的大學(xué)同學(xué)蚯根,身高196后众,大學(xué)四年一直是姓兔樱籃球隊(duì)員颅拦。 由于性格...
    cbedd1eb589c閱讀 350評(píng)論 0 1