當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 程序員小灰
[導(dǎo)讀]從問(wèn)題出發(fā),走一遍線程池的思想之旅,你會(huì)發(fā)現(xiàn)它很簡(jiǎn)單。

小宇:閃客,我最近看到線程池,被里邊亂七八槽的參數(shù)給搞暈了,你能不能給我講講呀?

閃客:沒(méi)問(wèn)題,這個(gè)我擅長(zhǎng),咱們從一個(gè)最簡(jiǎn)單的情況開始,假設(shè)有一段代碼,你希望異步執(zhí)行它,是不是要寫出這樣的代碼?

new Thread(r).start();
小宇:嗯嗯,最簡(jiǎn)單的寫法似乎就是這樣呢。閃客:這種寫法當(dāng)然可以完成功能,可是你這樣寫,老王這樣寫,老張也這樣寫,程序中到處都是這樣創(chuàng)建線程的方法,能不能寫一個(gè)統(tǒng)一的工具類讓大家調(diào)用呢?
小宇:可以的,感覺(jué)有一個(gè)統(tǒng)一的工具類,更優(yōu)雅一些。
閃客:那如果讓你來(lái)設(shè)計(jì)這個(gè)工具類,你會(huì)怎么寫呢?我先給你定一個(gè)接口,你來(lái)實(shí)現(xiàn)。
public interface Executor {
public void execute(Runnable r);
}
小宇:emmm,我可能先定義幾個(gè)成員變量,比如核心線程數(shù)、最大線程數(shù) ...反正就是那些亂七八糟的參數(shù)。閃客:STOP!小宇呀,你現(xiàn)在深受面試手冊(cè)的毒害,你先把這些全部的概念忘掉,就說(shuō)讓你寫一個(gè)最簡(jiǎn)單的工具類,第一反應(yīng),你會(huì)怎么寫?
第一版

小宇:那我可能會(huì)這樣

// 新線程:直接創(chuàng)建一個(gè)新線程運(yùn)行
class FlashExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
}
}
閃客:嗯嗯很好,你的思路非常棒。小宇:啊,我這個(gè)會(huì)不會(huì)太 low 了呀,我還以為你會(huì)罵我呢。

怎么會(huì), Doug Lea 大神在 JDK 源碼注釋中給出的就是這樣的例子,這是最根本的功能。你在這個(gè)基礎(chǔ)上,嘗試著優(yōu)化一下?

第二版

小宇:還能怎么優(yōu)化呢?這不已經(jīng)用一個(gè)工具類實(shí)現(xiàn)了異步執(zhí)行了嘛!

閃客:我問(wèn)你一個(gè)問(wèn)題,假如有 10000 個(gè)人都調(diào)用這個(gè)工具類提交任務(wù),那就會(huì)創(chuàng)建 10000 個(gè)線程來(lái)執(zhí)行,這肯定不合適吧!能不能控制一下線程的數(shù)量呢?
小宇:這不難,我可以把這個(gè)任務(wù) r 丟到一個(gè) tasks 隊(duì)列中,然后只啟動(dòng)一個(gè)線程,就叫它 Worker 線程吧,不斷從 tasks 隊(duì)列中取任務(wù),執(zhí)行任務(wù)。這樣無(wú)論調(diào)用者調(diào)用多少次,永遠(yuǎn)就只有一個(gè) Worker 線程在運(yùn)行,像這樣。


閃客:太棒了,這個(gè)設(shè)計(jì)有了三個(gè)重大的意義:
1. 控制了線程數(shù)量。
2. 隊(duì)列不但起到了緩沖的作用,還將任務(wù)的提交與執(zhí)行解耦了。
3. 最重要的一點(diǎn)是,解決了每次重復(fù)創(chuàng)建和銷毀線程帶來(lái)的系統(tǒng)開銷。

小宇:哇真的么,這么小的改動(dòng)有這么多意義呀。

閃客:那當(dāng)然,不過(guò)只有一個(gè)后臺(tái)的工作線程 Worker 會(huì)不會(huì)少了點(diǎn)?還有如果這個(gè) tasks 隊(duì)列滿了怎么辦呢?

第三版
小宇:哦,的確,只有一個(gè)線程在某些場(chǎng)景下是很吃力的,那我把 Worker 線程的數(shù)量增加?

閃客:沒(méi)錯(cuò),Worker 線程的數(shù)量要增加,但是具體數(shù)量要讓使用者決定,調(diào)用時(shí)傳入,就叫核心線程數(shù) corePoolSize 吧。
小宇:好的,那我這樣設(shè)計(jì)。
1. 初始化線程池時(shí),直接啟動(dòng) corePoolSize 個(gè)工作線程 Worker 先跑著。
2. 這些 Worker 就是死循環(huán)從隊(duì)列里取任務(wù)然后執(zhí)行。
3. execute 方法仍然是直接把任務(wù)放到隊(duì)列,但隊(duì)列滿了之后直接拋棄

閃客:太完美了,獎(jiǎng)勵(lì)你一塊費(fèi)列羅吧。
小宇:哈哈謝謝,那我先吃一會(huì)兒哈。
閃客:好,你邊吃我邊說(shuō)?,F(xiàn)在我們已經(jīng)實(shí)現(xiàn)了一個(gè)至少不那么丑陋的線程池了,但還有幾處小瑕疵,比如初始化的時(shí)候,就創(chuàng)建了一堆 Worker 線程在那空跑著,假如此時(shí)并沒(méi)有異步任務(wù)提交過(guò)來(lái)執(zhí)行,這就有點(diǎn)浪費(fèi)了。
小宇:哦好像是誒!
閃客:還有,你這隊(duì)列一滿,就直接把新任務(wù)丟棄了,這樣有些粗暴,能不能讓調(diào)用者自己決定該怎么處理呢?
小宇:哎呀,想不到我這么溫柔的妹紙居然寫出了這么粗暴的代碼。
閃客:額,你先把費(fèi)列羅咽下去吧。
第四版

小宇:我吃完了,現(xiàn)在腦子有點(diǎn)不夠用了,得先消化消化食物,要不你幫我分析分析吧。

閃客:好的,現(xiàn)在我們做出如下改進(jìn)。

1. 按需創(chuàng)建Worker:剛初始化線程池時(shí),不再立刻創(chuàng)建 corePoolSize 個(gè)工作線程,而是等待調(diào)用者不斷提交任務(wù)的過(guò)程中,逐漸把工作線程 Worker 創(chuàng)建出來(lái),等數(shù)量達(dá)到 corePoolSize 時(shí)就停止,把任務(wù)直接丟到隊(duì)列里。那就必然要用一個(gè)屬性記錄已經(jīng)創(chuàng)建出來(lái)的工作線程數(shù)量,就叫 workCount 吧。

2. 加拒絕策略:實(shí)現(xiàn)上就是增加一個(gè)入?yún)ⅲ愋褪且粋€(gè)接口 RejectedExecutionHandler,由調(diào)用者決定實(shí)現(xiàn)類,以便在任務(wù)提交失敗后執(zhí)行 rejectedExecution 方法。
3. 增加線程工廠:實(shí)現(xiàn)上就是增加一個(gè)入?yún)?,類型是一個(gè)接口 ThreadFactory,增加工作線程時(shí)不再直接 new 線程,而是調(diào)用這個(gè)由調(diào)用者傳入的 ThreadFactory 實(shí)現(xiàn)類的 newThread 方法。
就像下面這樣。

小宇:哇,還是你厲害,這一版應(yīng)該很完美了吧?閃客:不不不,離完美還差得很遠(yuǎn),接下來(lái)的改進(jìn),由你來(lái)想吧,我這里可以給你一個(gè)提示彈性思維

第五版

小宇:彈性思維?哈哈閃客你這術(shù)語(yǔ)說(shuō)的真是越來(lái)越不像人話了閃客:咳咳小宇:哦,我是說(shuō)你肯定是指我這個(gè)代碼寫的沒(méi)有彈性,對(duì)吧?可是彈性是指什么呢?閃客:簡(jiǎn)單說(shuō),在這個(gè)場(chǎng)景里,彈性就是在任務(wù)提交比較頻繁,和任務(wù)提交非常不頻繁這兩種情況下,你這個(gè)代碼是否有問(wèn)題?小宇:emmm 讓我想想,我這個(gè)線程池,當(dāng)提交任務(wù)的量突增時(shí),工作線程和隊(duì)列都被占滿了,就只能走拒絕策略,其實(shí)就是被丟棄掉閃客:是的小宇:這樣的確是太硬了,誒不過(guò)我想了下,調(diào)用方可以通過(guò)設(shè)置很大的核心線程數(shù) corePoolSize 來(lái)解決這個(gè)問(wèn)題呀。閃客:的確是可以,但一般場(chǎng)景下 QPS 高峰期都很短,而為了這個(gè)很短暫的高峰,設(shè)置很大的核心線程數(shù),簡(jiǎn)直太浪費(fèi)資源了,你看上面的圖不覺(jué)得眼暈么?小宇:是呀,那怎么辦呢,太大了也不行,太小了也不行。閃客:我們可以發(fā)明一個(gè)新的屬性,叫最大線程數(shù) maximumPoolSize。當(dāng)核心線程數(shù)和隊(duì)列都滿了時(shí),新提交的任務(wù)仍然可以通過(guò)創(chuàng)建新的工作線程(叫它非核心線程),直到工作線程數(shù)達(dá)到 maximumPoolSize 為止,這樣就可以緩解一時(shí)的高峰期了,而用戶也不用設(shè)置過(guò)大的核心線程數(shù)。

小宇:哦好像有點(diǎn)感覺(jué)了,可是具體怎么操作呢?閃客:想象力不行呀小宇,那你看下面的演示。
1. 開始的時(shí)候和上一版一樣,當(dāng) workCount < corePoolSize 時(shí),通過(guò)創(chuàng)建新的 Worker 來(lái)執(zhí)行任務(wù)。
2. 當(dāng) workCount >= corePoolSize 就停止創(chuàng)建新線程,把任務(wù)直接丟到隊(duì)列里。
3. 但當(dāng)隊(duì)列已滿且仍然 workCount < maximumPoolSize 時(shí),不再直接走拒絕策略,而是創(chuàng)建非核心線程,直到 workCount = maximumPoolSize,再走拒絕策略。
小宇:哎呀,我怎么沒(méi)想到,這樣 corePoolSize 就負(fù)責(zé)平時(shí)大多數(shù)情況所需要的工作線程數(shù),而 maximumPoolSize 就負(fù)責(zé)在高峰期臨時(shí)擴(kuò)充工作線程數(shù)。閃客:沒(méi)錯(cuò),高峰時(shí)期的彈性搞定了,那自然就還要考慮低谷時(shí)期。當(dāng)長(zhǎng)時(shí)間沒(méi)有任務(wù)提交時(shí),核心線程與非核心線程都一直空跑著,浪費(fèi)資源。我們可以給非核心線程設(shè)定一個(gè)超時(shí)時(shí)間 keepAliveTime,當(dāng)這么長(zhǎng)時(shí)間沒(méi)能從隊(duì)列里獲取任務(wù)時(shí),就不再等了,銷毀線程。


小宇:嗯,這回咱們的線程池在 QPS 高峰時(shí)可以臨時(shí)擴(kuò)容,QPS 低谷時(shí)又可以及時(shí)回收線程(非核心線程)而不至于浪費(fèi)資源,真的顯得十分 Q 彈呢。閃客:是呀是呀。誒不對(duì),怎么又變成我說(shuō)了,不是說(shuō)這一版你來(lái)思考么?小宇:我也想啊,但你這一講技術(shù)就自說(shuō)自話的毛病老是不改,我有啥辦法。閃客:額抱歉抱歉,那接下來(lái)你總結(jié)一下我們的線程池吧

總結(jié)

小宇:嗯好的,首先它的構(gòu)造方法是這個(gè)樣子滴public FlashExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueueworkQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

{
... // 省略一些參數(shù)校驗(yàn)
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}

這些參數(shù)分別是
int corePoolSize:核心線程數(shù)int maximumPoolSize:最大線程數(shù)long keepAliveTime:非核心線程的空閑時(shí)間TimeUnit unit:空閑時(shí)間的單位BlockingQueueworkQueue:任務(wù)隊(duì)列(線程安全的阻塞隊(duì)列)ThreadFactory threadFactory:線程工廠RejectedExecutionHandler handler:拒絕策略整個(gè)任務(wù)的提交流程是閃客:不錯(cuò)不錯(cuò),這可是你自己總結(jié)的喲,現(xiàn)在還用我給你講什么是線程池了么?小宇:啊天呢,我才發(fā)現(xiàn)這似乎就是我一直弄不清楚的線程池的參數(shù)和原理呢!閃客:沒(méi)錯(cuò),而且最后一版代碼的構(gòu)造方法,就是 Java 面試常考的 ThreadPoolExecutor 最長(zhǎng)的那個(gè)構(gòu)造方法,參數(shù)名都沒(méi)變。小宇:哇,太贊了!我都忘了一開始我想干嘛了,嘻嘻。閃客:哈哈,不知不覺(jué)學(xué)到了技術(shù)才爽呢,對(duì)吧?晚飯時(shí)間快到了,要不要一塊去吃山西面館呀?小宇:哦,那家店餐桌的顏色我不太喜歡,下次吧。閃客:哦好吧。

后記
線程池是面試??嫉闹R(shí)點(diǎn),網(wǎng)上很多文章都是直接從它那有 7 個(gè)參數(shù)的構(gòu)造方法講起,強(qiáng)行把各個(gè)參數(shù)的含義說(shuō)給你聽,讓人云里霧里。希望讀者讀完本篇文章后,線程池的這些參數(shù)不再是死記硬背,而是像本文中這些動(dòng)圖一樣在你的腦中活靈活現(xiàn),這樣就能永遠(yuǎn)記住他們啦~

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉