国产av一二三区|日本不卡动作网站|黄色天天久久影片|99草成人免费在线视频|AV三级片成人电影在线|成年人aV不卡免费播放|日韩无码成人一级片视频|人人看人人玩开心色AV|人妻系列在线观看|亚洲av无码一区二区三区在线播放

網(wǎng)易首頁 > 網(wǎng)易號(hào) > 正文 申請(qǐng)入駐

面試官一個(gè)線程池問題把我問懵逼了

0
分享至

原創(chuàng) why技術(shù)

前幾天,有個(gè)朋友在微信上找我。他問:why哥,在嗎?

我說:發(fā)生腎么事了?

他啪的一下就提了一個(gè)問題啊,很快。

我大意了,隨意瞅了一眼,這題不是很簡(jiǎn)單嗎?

結(jié)果沒想到里面還隱藏著一篇文章。

故事,得從這個(gè)問題說起:

上面的圖中的線程池配置是這樣的:

ExecutorService executorService = new ThreadPoolExecutor(40, 80, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(100), new DefaultThreadFactory("test"), new ThreadPoolExecutor.DiscardPolicy());

上面這個(gè)線程池里面的參數(shù)、執(zhí)行流程啥的我就不再解釋了。

畢竟我曾經(jīng)在《一人血書,想讓why哥講一下這道面試題?!愤@篇文章里面發(fā)過毒誓的,再說就是小王吧了:

上面的這個(gè)問題其實(shí)就是一個(gè)非常簡(jiǎn)單的八股文問題:

非核心線程在什么時(shí)候被回收?

如果經(jīng)過 keepAliveTime 時(shí)間后,超過核心線程數(shù)的線程還沒有接受到新的任務(wù),就會(huì)被回收。

標(biāo)準(zhǔn)答案,完全沒毛病。

那么我現(xiàn)在帶入一個(gè)簡(jiǎn)單的場(chǎng)景,為了簡(jiǎn)單直觀,我們把線程池相關(guān)的參數(shù)調(diào)整一下:

ExecutorService executorService = new ThreadPoolExecutor(2, 3, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>(2), new DefaultThreadFactory("test"), new ThreadPoolExecutor.DiscardPolicy());

那么問題來了:

這個(gè)線程最多能容納的任務(wù)是不是 5 個(gè)?
假設(shè)任務(wù)需要執(zhí)行 1 秒鐘,那么我直接循環(huán)里面提交 5 個(gè)任務(wù)到線程池,肯定是在 1 秒鐘之內(nèi)提交完成,那么當(dāng)前線程池的活躍線程是不是就是 3 個(gè)?
如果接下來的 30 秒,沒有任務(wù)提交過來。那么 30 秒之后,當(dāng)前線程池的活躍線程是不是就是 2 個(gè)?

上面這三個(gè)問題的答案都是肯定的,如果你搞不明白為什么,那么我建議你先趕緊去補(bǔ)充一下線程池相關(guān)的知識(shí)點(diǎn),下面的內(nèi)容你強(qiáng)行看下去肯定是一臉懵逼的。

接下來的問題是這樣的:

如果當(dāng)前線程池的活躍線程是 3 個(gè)(2 個(gè)核心線程+ 1 個(gè)非核心線程),但是它們各自的任務(wù)都執(zhí)行完成了,都處于 waiting 狀態(tài)。然后我每隔 3 秒往線程池里面扔一個(gè)耗時(shí) 1 秒的任務(wù)。那么 30 秒之后,活躍線程數(shù)是多少?

先說答案:還是 3 個(gè)。

從我個(gè)人正常的思維,是這樣的:核心線程是空閑的,每隔 3 秒扔一個(gè)耗時(shí) 1 秒的任務(wù)過來,所以僅需要一個(gè)核心線程就完全處理的過來。

那么,30 秒內(nèi),超過核心線程的那一個(gè)線程一直處于等待狀態(tài),所以 30 秒之后,就被回收了。 但是上面僅僅是我的主觀認(rèn)為,而實(shí)際情況呢?

30 秒之后,超過核心線程
的線程并不會(huì)被回收,活躍線程還是 3 個(gè)。 到這里,如果你知道是 3 個(gè),且知道為什么是 3 個(gè),即了解為什么非核心線程并沒有被回收,那么接下里的內(nèi)容應(yīng)該就是你已經(jīng)掌握的了。

可以不看,拉到最后,點(diǎn)個(gè)贊,去忙自己的事情吧。

如果你不知道,可以接著看,了解一下為什么是 3 個(gè)。

雖然我相信沒有面試官會(huì)問這樣的問題,但是對(duì)于你去理解線程池,是有幫助的。

先上 Demo

基于我前面說的這個(gè)場(chǎng)景,碼出代碼如下:

public class ThreadTest { @Test public void test() throws InterruptedException { ThreadPoolExecutor executorService = new ThreadPoolExecutor(2, 3, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>(2), new DefaultThreadFactory("test"), new ThreadPoolExecutor.DiscardPolicy()); //每隔兩秒打印線程池的信息 ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); scheduledExecutorService.scheduleAtFixedRate(() -> { System.out.println("=====================================thread-pool-info:" + new Date() + "====================================="); System.out.println("CorePoolSize:" + executorService.getCorePoolSize()); System.out.println("PoolSize:" + executorService.getPoolSize()); System.out.println("ActiveCount:" + executorService.getActiveCount()); System.out.println("KeepAliveTime:" + executorService.getKeepAliveTime(TimeUnit.SECONDS)); System.out.println("QueueSize:" + executorService.getQueue().size()); }, 0, 2, TimeUnit.SECONDS); try { //同時(shí)提交5個(gè)任務(wù),模擬達(dá)到最大線程數(shù) for (int i = 0; i < 5; i++) { executorService.execute(new Task()); } } catch (Exception e) { e.printStackTrace(); } //休眠10秒,打印日志,觀察線程池狀態(tài) Thread.sleep(10000); //每隔3秒提交一個(gè)任務(wù) while (true) { Thread.sleep(3000); executorService.submit(new Task()); } } static class Task implements Runnable { @Override public void run(){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread() + "-執(zhí)行任務(wù)"); } }}

這份代碼也是提問的哥們給我的,我做了微調(diào),你直接粘出去就能跑起來。

show me code,no bb。這才是相互探討的正確姿勢(shì)。

這個(gè)程序的運(yùn)行結(jié)果是這樣的:

一共五個(gè)任務(wù),線程池的運(yùn)行情況是什么樣的呢?

先看標(biāo)號(hào)為 ① 的地方:

三個(gè)線程都在執(zhí)行任務(wù),然后 2 號(hào)線程和 1 號(hào)線程率先完成了任務(wù),接著把隊(duì)列里面的兩個(gè)任務(wù)拿出來執(zhí)行(標(biāo)號(hào)為 ② 的地方)。

按照程序,接下來,每隔 3 秒就有一個(gè)耗時(shí) 1 秒的任務(wù)過來。而此時(shí)線程池里面的三個(gè)活躍線程都是空閑狀態(tài)。

那么問題就來了:

該選擇哪個(gè)線程來執(zhí)行這個(gè)任務(wù)呢?是隨機(jī)選一個(gè)嗎?

雖然接下來的程序還沒有執(zhí)行,但是基于前面的截圖,我現(xiàn)在就可以告訴你,接下來的任務(wù),線程執(zhí)行順序?yàn)椋?/p>

Thread[test-1-3,5,main]-執(zhí)行任務(wù)
Thread[test-1-2,5,main]-執(zhí)行任務(wù)
Thread[test-1-1,5,main]-執(zhí)行任務(wù)
Thread[test-1-3,5,main]-執(zhí)行任務(wù)
Thread[test-1-2,5,main]-執(zhí)行任務(wù)
Thread[test-1-1,5,main]-執(zhí)行任務(wù)
......

即雖然線程都是空閑的,但是當(dāng)任務(wù)來的時(shí)候不是隨機(jī)調(diào)用的,而是輪詢。

由于是輪詢,每三秒執(zhí)行一次,所以非核心線程的空閑時(shí)間最多也就是 9 秒,不會(huì)超過 30 秒,所以一直不會(huì)被回收。

基于這個(gè) Demo,我們就從表象上回答了,為什么活躍線程數(shù)一直為 3。

為什么是輪詢?

我們通過 Demo 驗(yàn)證了上面場(chǎng)景中,線程執(zhí)行順序?yàn)檩喸儭?/p>

那么為什么呢?

這只是通過日志得出的表象呀,內(nèi)部原理呢?對(duì)應(yīng)的代碼呢?

這一小節(jié)帶大家看一下到底是怎么回事。

首先我看到這個(gè)表象的時(shí)候我就猜測(cè):這三個(gè)線程肯定是在某個(gè)地方被某個(gè)隊(duì)列存起來了,基于此,才能實(shí)現(xiàn)輪詢調(diào)用。

所以,我一直在找這個(gè)隊(duì)列,一直沒有找到對(duì)應(yīng)的代碼,我還有點(diǎn)著急了。想著不會(huì)是在操作系統(tǒng)層面控制的吧?

后來我冷靜下來,覺得不太可能。于是電光火石之間,我想到了,要不先 Dump 一下線程,看看它們都在干啥:

Dump 之后,這玩意我眼熟啊,AQS 的等待隊(duì)列啊。

根據(jù)堆棧信息,我們可以定位到這里的源碼:

java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject#awaitNanos

看到這里的時(shí)候,我才一下恍然大悟了起來。

害,是自己想的太多了。

說穿了,這其實(shí)就是個(gè)生產(chǎn)者-消費(fèi)者的問題啊。

三個(gè)線程就是三個(gè)消費(fèi)者,現(xiàn)在沒有任務(wù)需要處理,它們就等著生產(chǎn)者生產(chǎn)任務(wù),然后通知它們準(zhǔn)備消費(fèi)。

由于本文只是帶著你去找答案在源碼的什么地方,不對(duì)源碼進(jìn)行解讀。

所以我默認(rèn)你是對(duì) AQS 是有一定的了解的。

可以看到 addConditionWaiter 方法其實(shí)就是在操作我們要找的那個(gè)隊(duì)列。學(xué)名叫做等待隊(duì)列。

Debug 一下,看看隊(duì)列里面的情況:

巧了嘛,這不是。順序剛好是:

Thread[test-1-3,5,main]
Thread[test-1-2,5,main]
Thread[test-1-1,5,main]

消費(fèi)者這邊我們大概摸清楚了,接著去看看生產(chǎn)者。

java.util.concurrent.ThreadPoolExecutor#execute

線程池是在這里把任務(wù)放到隊(duì)列里面去的。

而這個(gè)方法里面的源碼是這樣的:

其中signalNotEmpty() 最終會(huì)走到 doSignal 方法,而該方法里面會(huì)調(diào)用 transferForSignal 方法。

這個(gè)方法里面會(huì)調(diào)用 LockSupport.unpark(node.thred) 方法,喚醒線程:

而喚醒的順序,就是等待隊(duì)列里面的順序:

所以,現(xiàn)在你知道當(dāng)一個(gè)任務(wù)來了之后,這個(gè)任務(wù)該由線程池里面的哪個(gè)線程執(zhí)行,這個(gè)不是隨機(jī)的,也不是隨便來的。

是講究一個(gè)順序的。

什么順序呢?

Condition 里面的等待隊(duì)列里面的順序。

什么,你不太懂 Condition?

那還不趕緊去學(xué)?等著我給你講呢?

本來我是想寫一下的,后來發(fā)現(xiàn)《Java并發(fā)編程的藝術(shù)》一書中的 5.6.2 小節(jié)已經(jīng)寫的挺清楚了,圖文并茂。這部分內(nèi)容其實(shí)也是面試的時(shí)候的高頻考點(diǎn),所以自己去看看就好了。

先欠著,欠著。

非核心線程怎么回收?

還是上面的例子,假設(shè)非核心線程就空閑了超過 30 秒,那么它是怎么被回收的呢?

這個(gè)也是一個(gè)比較熱門的面試題。

這題沒有什么高深的地方,答案就藏在源碼的這個(gè)地方:

java.util.concurrent.ThreadPoolExecutor#getTask

當(dāng) timed 參數(shù)為 true 的時(shí)候,會(huì)執(zhí)行 workQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS) 方法。

而 timed 什么時(shí)候?yàn)?true 呢?

boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

allowCoreThreadTimeOut 默認(rèn)為 false。

所以,就是看 wc > corePoolSize 條件,wc 是活躍線程數(shù)。此時(shí)活躍線程數(shù)為 3 ,大于核心線程數(shù) 2。

因此 timed 為 true。

也就是說,當(dāng)前 workQueue 為空的時(shí)候,現(xiàn)在三個(gè)線程都阻塞 workQueue.poll 方法中。

而當(dāng)指定時(shí)間后,workQueue 還是為空,則返回為 null。

于是在 1077 行把 timeOut 修改為 true。

進(jìn)入一下次循環(huán),返回 null。

最終會(huì)執(zhí)行到這個(gè)方法:

java.util.concurrent.ThreadPoolExecutor#processWorkerExit

而這個(gè)方法里面會(huì)執(zhí)行 remove 的操作。

于是線程就被回收了。

所以當(dāng)超過指定時(shí)間后,線程會(huì)被回收。

那么被回收的這個(gè)線程是核心線程還是非核心線程呢?

不知道。

因?yàn)樵诰€程池里面,核心線程和非核心線程僅僅是一個(gè)概念而已,其實(shí)拿著一個(gè)線程,我們并不能知道它是核心線程還是非核心線程。

這個(gè)地方就是一個(gè)證明,因?yàn)楫?dāng)工作線程多余核心線程數(shù)之后,所有的線程都在 poll,也就是說所有的線程都有可能被回收:

另外一個(gè)強(qiáng)有力的證明就是 addWorker 這里:

core 參數(shù)僅僅是控制取 corePoolSize 還是 maximumPoolSize。

所以,這個(gè)問題你說怎么回答:

JDK 區(qū)分的方式就是不區(qū)分。

那么我們可以知道嗎?

可以,比如通過觀察日志,前面的案例中,我就知道這兩個(gè)是核心線程,因?yàn)樗鼈冏钕葎?chuàng)建:

Thread[test-1-1,5,main]-執(zhí)行任務(wù)
Thread[test-1-2,5,main]-執(zhí)行任務(wù)

在程序里面怎么知道呢?

目前是不知道的,但是這個(gè)需求,加錢就可以實(shí)現(xiàn)。

自己擴(kuò)展一下線程池嘛,給線程池里面的線程打個(gè)標(biāo)還不是一件很簡(jiǎn)單的事情嗎?

只是你想想,你區(qū)分這玩意干啥,有沒有可落地的需求?

畢竟,脫離需求談實(shí)現(xiàn)。都是耍流氓。

最后說一句

才疏學(xué)淺,難免會(huì)有紕漏,如果你發(fā)現(xiàn)了錯(cuò)誤的地方,可以在后臺(tái)提出來,我對(duì)其加以修改。

特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺(tái)“網(wǎng)易號(hào)”用戶上傳并發(fā)布,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相關(guān)推薦
熱點(diǎn)推薦
島內(nèi)最新民調(diào),事關(guān)兩岸統(tǒng)一,蔣萬安鄭麗文支持率有驚人變化?

島內(nèi)最新民調(diào),事關(guān)兩岸統(tǒng)一,蔣萬安鄭麗文支持率有驚人變化?

DS北風(fēng)
2026-04-23 15:56:17
為啥清朝才滅亡一百余年,滿語就迅速消亡了?原因很簡(jiǎn)單

為啥清朝才滅亡一百余年,滿語就迅速消亡了?原因很簡(jiǎn)單

凡人侃史
2026-04-26 18:22:35
23歲門外漢攜ChatGPT,攻克60年數(shù)學(xué)猜想!陶哲軒:我們?nèi)咂?>
    </a>
        <h3>
      <a href=新智元
2026-04-26 18:06:15
靠“漢芯一號(hào)”騙取11億研究經(jīng)費(fèi),后逃到美國的陳進(jìn),結(jié)局如何?

靠“漢芯一號(hào)”騙取11億研究經(jīng)費(fèi),后逃到美國的陳進(jìn),結(jié)局如何?

阿鳧愛吐槽
2026-04-14 02:01:01
WTA又爆冷!8號(hào)種子0-2慘遭橫掃,鄭欽文2-1大逆轉(zhuǎn),將戰(zhàn)世界第2

WTA又爆冷!8號(hào)種子0-2慘遭橫掃,鄭欽文2-1大逆轉(zhuǎn),將戰(zhàn)世界第2

不想長(zhǎng)大的莫扎特
2026-04-26 13:08:34
神兵天降!道蘇姆43分創(chuàng)歷史,森林狼連傷兩將仍3-1打崩掘金

神兵天降!道蘇姆43分創(chuàng)歷史,森林狼連傷兩將仍3-1打崩掘金

仰臥撐FTUer
2026-04-26 19:11:26
三峽大壩蓄水近22年,成了魚類的天堂,如今里面最大的魚有多大?

三峽大壩蓄水近22年,成了魚類的天堂,如今里面最大的魚有多大?

探源歷史
2026-04-03 16:55:38
白宮記協(xié)晚宴槍手目標(biāo)曝光 現(xiàn)場(chǎng)畫面顯示多名高官匆忙撤離

白宮記協(xié)晚宴槍手目標(biāo)曝光 現(xiàn)場(chǎng)畫面顯示多名高官匆忙撤離

新華社
2026-04-26 17:36:42
笑不活了!女孩把雞畫得圓肥被判不合格,家長(zhǎng)把雞的照片發(fā)給老師

笑不活了!女孩把雞畫得圓肥被判不合格,家長(zhǎng)把雞的照片發(fā)給老師

夜深愛雜談
2026-04-25 07:33:34
湖人在G3擊敗火箭后,宣布關(guān)于勒布朗·詹姆斯的重要消息!

湖人在G3擊敗火箭后,宣布關(guān)于勒布朗·詹姆斯的重要消息!

夜白侃球
2026-04-25 23:54:45
1973年江青私闖毛主席住處,毛主席怒斥:隨便闖,再闖,就抓起來

1973年江青私闖毛主席住處,毛主席怒斥:隨便闖,再闖,就抓起來

南鈦
2026-04-26 09:20:22
有沒有人敢爆自己的瓜?網(wǎng)友:確定玩這么大嗎?

有沒有人敢爆自己的瓜?網(wǎng)友:確定玩這么大嗎?

夜深愛雜談
2026-02-18 20:55:58
民進(jìn)黨高層竟敢偷偷離臺(tái),大陸火速行動(dòng)!美國來陰的,鄭麗文拼了

民進(jìn)黨高層竟敢偷偷離臺(tái),大陸火速行動(dòng)!美國來陰的,鄭麗文拼了

致敬明天的太陽
2026-04-26 17:14:46
隨著森林狼3-1掘金,雷霆3-0,火箭0-3,西部季后賽4強(qiáng)基本出爐了

隨著森林狼3-1掘金,雷霆3-0,火箭0-3,西部季后賽4強(qiáng)基本出爐了

小火箭愛體育
2026-04-26 11:36:11
61歲何智麗現(xiàn)狀:從日本回老家上海,與老友聚餐,面色紅潤(rùn)沒發(fā)福

61歲何智麗現(xiàn)狀:從日本回老家上海,與老友聚餐,面色紅潤(rùn)沒發(fā)福

以茶帶書
2026-04-14 14:09:22
寶視達(dá)回應(yīng)“鏡片裝反孩子近視暴漲到900度”:出廠交付時(shí)不存在鏡片裝反情況,愿退還配鏡費(fèi)用贈(zèng)送1000元眼鏡及感謝金

寶視達(dá)回應(yīng)“鏡片裝反孩子近視暴漲到900度”:出廠交付時(shí)不存在鏡片裝反情況,愿退還配鏡費(fèi)用贈(zèng)送1000元眼鏡及感謝金

極目新聞
2026-04-26 09:13:09
就在剛剛!廣東德比賽前傳來3大壞消息,杜鋒這回真笑不出來了

就在剛剛!廣東德比賽前傳來3大壞消息,杜鋒這回真笑不出來了

林子說事
2026-04-26 11:16:05
名嘴:在詹姆斯單核帶隊(duì)25分準(zhǔn)三雙、3比0后,詹黑都跑哪去了?

名嘴:在詹姆斯單核帶隊(duì)25分準(zhǔn)三雙、3比0后,詹黑都跑哪去了?

歷史第一人梅西
2026-04-26 18:16:58
戰(zhàn)國七雄中,為何只有“韓國”的國號(hào),沒有再被后世啟用過?

戰(zhàn)國七雄中,為何只有“韓國”的國號(hào),沒有再被后世啟用過?

爆史君帶你讀歷史
2026-04-24 18:02:29
“擋住隔壁商戶她會(huì)報(bào)警” ,上海一家老牌生煎店告示語出驚人,鄰居們卻給出好評(píng)……

“擋住隔壁商戶她會(huì)報(bào)警” ,上海一家老牌生煎店告示語出驚人,鄰居們卻給出好評(píng)……

新聞晨報(bào)隨申Hi
2026-04-26 18:00:24
2026-04-26 19:32:49
軟測(cè)試驗(yàn)田
軟測(cè)試驗(yàn)田
軟件測(cè)試領(lǐng)域最新最前沿資訊
117文章數(shù) 128關(guān)注度
往期回顧 全部

科技要聞

漲價(jià)浪潮下,DeepSeek推動(dòng)AI“價(jià)格戰(zhàn)”

頭條要聞

特朗普內(nèi)閣又一女部長(zhǎng)落馬:強(qiáng)迫男下屬為其提供性服務(wù)

頭條要聞

特朗普內(nèi)閣又一女部長(zhǎng)落馬:強(qiáng)迫男下屬為其提供性服務(wù)

體育要聞

森林狼3比1掘金:逆境中殺出了多孫穆?!

娛樂要聞

僅次《指環(huán)王》的美劇,有第二季

財(cái)經(jīng)要聞

事關(guān)新就業(yè)群體,中辦、國辦發(fā)文

汽車要聞

預(yù)售19.38萬元起 哈弗猛龍PLUS七座版亮相

態(tài)度原創(chuàng)

健康
教育
本地
親子
公開課

干細(xì)胞如何讓燒燙傷皮膚"再生"?

教育要聞

休學(xué)率上漲,驚到很多人!北大六院醫(yī)生直言:這背后其實(shí)是夫妻關(guān)系和家庭關(guān)系……

本地新聞

云游中國|逛世界風(fēng)箏都 留學(xué)生探秘中國傳統(tǒng)文化

親子要聞

給寶寶的輔食太精細(xì),危害不是一般大,家長(zhǎng)們?cè)缌私庠绨残?/h3>

公開課

李玫瑾:為什么性格比能力更重要?

無障礙瀏覽 進(jìn)入關(guān)懷版