美團(tuán)面試官:核心線程數(shù)為0時(shí),線程池如何執(zhí)行?
線程池是 Java 中用于提升程序執(zhí)行效率的主要手段,也是并發(fā)編程中的核心實(shí)現(xiàn)技術(shù),并且它也被廣泛的應(yīng)用在日常項(xiàng)目的開(kāi)發(fā)之中。那問(wèn)題來(lái)了,如果把線程池中的核心線程數(shù)設(shè)置為 0 時(shí),線程池是如何執(zhí)行的?
要回答這個(gè)問(wèn)題,我們首先要了解在正常情況下,線程池的執(zhí)行流程,也就是說(shuō)當(dāng)有一個(gè)任務(wù)來(lái)了之后,線程池是如何運(yùn)行的?
1.線程池的執(zhí)行流程
正常情況下(核心線程數(shù)不為 0 的情況下)線程池的執(zhí)行流程如下:
- 判斷核心線程數(shù):先判斷當(dāng)前工作線程數(shù)是否大于核心線程數(shù),如果結(jié)果為 false,則新建線程并執(zhí)行任務(wù)。
- 判斷任務(wù)隊(duì)列:如果大于核心線程數(shù),則判斷任務(wù)隊(duì)列是否已滿?如果結(jié)果為 false,則把任務(wù)添加到任務(wù)隊(duì)列中等待線程執(zhí)行。
- 判斷最大線程數(shù):如果任務(wù)隊(duì)列已滿,則判斷當(dāng)前線程數(shù)量是否超過(guò)最大線程數(shù)?如果結(jié)果為 false,則新建線程執(zhí)行此任務(wù)。
- 判斷是否要執(zhí)行拒絕策略:如果超過(guò)最大線程數(shù),則將執(zhí)行線程池的拒絕策略。
如下圖所示:
圖片
核心線程數(shù) VS 最大線程數(shù)
核心線程數(shù)(corePoolSize)和最大線程數(shù)(maximumPoolSize)都是線程池中的兩個(gè)重要參數(shù),其中:
- 核心線程數(shù)定義了線程池中最小線程數(shù)量,即使這些線程處于空閑狀態(tài),也不會(huì)被銷毀。
- 最大線程數(shù)定義了線程池中允許的最大線程數(shù)量,最大線程數(shù)等于核心線程數(shù) + 臨時(shí)線程數(shù),最大線程數(shù)主要是提供了一種機(jī)制來(lái)應(yīng)對(duì)突發(fā)的高并發(fā)請(qǐng)求,當(dāng)有大量任務(wù)的時(shí)候,可以創(chuàng)建線程數(shù)量的上線。
PS:在線程池的使用過(guò)程中,最大線程數(shù)必須大于等于核心線程數(shù),否則程序執(zhí)行會(huì)報(bào)錯(cuò)。
2.核心線程為0的執(zhí)行流程
那么問(wèn)題來(lái)了,按照線程池的正常執(zhí)行流程來(lái)看,如果核心線程數(shù)為 0 的話,那么當(dāng)任務(wù)來(lái)了之后會(huì)判斷當(dāng)前工作的線程數(shù)不大于核心線程數(shù),那也就不會(huì)創(chuàng)建線程執(zhí)行任務(wù)了,會(huì)將任務(wù)放到隊(duì)列。
但這個(gè)結(jié)果又很滑稽,有任務(wù)來(lái)了線程池竟然不執(zhí)行,而是先放到任務(wù)隊(duì)列中,這好像有比較奇怪,這就好比你開(kāi)了一個(gè)快遞店,當(dāng)有快遞來(lái)了之后,你想的不是如何派送,而是直接把它丟到倉(cāng)庫(kù)一樣滑稽,這會(huì)讓等快遞的人很著急,所以,我們需要驗(yàn)證一下線程池的執(zhí)行是否如我們猜想的那般,驗(yàn)證代碼如下:
// 線程池核心線程數(shù)設(shè)置為 0
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
0, 10, 1, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10));
// 給線程池添加任務(wù)
threadPoolExecutor.execute(() -> {
System.out.println("www.javacn.site");
});
最終程序的執(zhí)行結(jié)果如下:
圖片
咦,結(jié)果怎么不符合我們的預(yù)期呢?為什么任務(wù)來(lái)了之后,沒(méi)有將任務(wù)放到任務(wù)隊(duì)列而是直接執(zhí)行了呢?
雖然程序執(zhí)行結(jié)果符合正常邏輯,但又和我們了解的線程池執(zhí)行理論相違背,這是什么原因呢?
查看線程池的執(zhí)行源碼
帶著這個(gè)疑問(wèn),我們查看了線程池的執(zhí)行源碼發(fā)現(xiàn),線程池的執(zhí)行過(guò)程遠(yuǎn)比我們想想的復(fù)雜,線程池核心源碼如下:
圖片
從上面源碼可以看出,當(dāng)我們將任務(wù)添加到隊(duì)列的時(shí)候,線程池會(huì)判斷工作的線程數(shù)是否為 0,也就是上面圈出來(lái)的那行代碼,如果當(dāng)前工作線程為 0 的話,會(huì)創(chuàng)建線程執(zhí)行任務(wù)。哦,原來(lái)如此,這樣,就能將理論和實(shí)踐對(duì)應(yīng)上了。
也就是說(shuō),當(dāng)核心線程數(shù)為 0 時(shí),當(dāng)來(lái)了一個(gè)任務(wù)之后,會(huì)先將任務(wù)添加到任務(wù)隊(duì)列,同時(shí)也會(huì)判斷當(dāng)前工作的線程數(shù)是否為 0,如果為 0,則會(huì)創(chuàng)建線程來(lái)執(zhí)行線程池的任務(wù),這就是正確的線程池執(zhí)行流程,同時(shí)也是面試官想要的答案。