[014]你想自己設(shè)計(jì)一個(gè)線程池么

背景

作為幾年工作經(jīng)驗(yàn)的java程序員肯定知道java中通過(guò)線程池來(lái)調(diào)度線程的。線程池分為幾種需五,為什么會(huì)設(shè)計(jì)這幾種線程池各自的實(shí)現(xiàn)算法是什么讼撒,適用場(chǎng)景是什么?這些疑問(wèn)其實(shí)是脫離java語(yǔ)言摹闽,其他語(yǔ)言設(shè)計(jì)線程池也會(huì)遇到同樣的問(wèn)題蹄咖。所以這里對(duì)這線程池設(shè)計(jì)原理需要考慮的方面進(jìn)行分析。

線程池的作用

1.線程池即預(yù)先創(chuàng)建線程的技術(shù)付鹿,一個(gè)線程執(zhí)行完后重新放回不會(huì)銷毀掉提高了線程的利用率澜汤。
2.由于我們要使用線程來(lái)執(zhí)行任務(wù)的時(shí)候直接從線程池中去現(xiàn)成的所以提高了程序的相應(yīng)速度蚜迅。
3.線程池可以對(duì)里面的線程進(jìn)行管理,至于如何管理XXXX(如何銷毀線程俊抵、如何結(jié)束線程狀態(tài)等等)谁不。

創(chuàng)建線程池需要考慮的

從這里我們知道線程池的一些基本配置參數(shù)。比如 線程池的大小徽诲,執(zhí)行任務(wù)隊(duì)列刹帕,線程池滿了新任務(wù)的執(zhí)行策略,工作線程空閑后存活時(shí)間(如果想提高線程利用率提議調(diào)大該時(shí)間)谎替。
所以它的構(gòu)造函數(shù)為:

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) 

這里為什么有一個(gè)corePoolSize和maximumPoolSize呢偷溺? 級(jí)別關(guān)系是 coreSize -> 隊(duì)列 ->(無(wú)法使用隊(duì)列則創(chuàng)建新線程) maximumPoolSize。

a).corePoolSize意思是基本大小钱贯,比如線程池corePoolSize=10挫掏,而此時(shí)線程池里有5個(gè)線程且都是空閑的,由于還沒(méi)有達(dá)到corePoolSize秩命,如果提交一個(gè)任務(wù)會(huì)從線程池里選擇一個(gè)線程來(lái)執(zhí)行任務(wù)砍濒。當(dāng)達(dá)到了corePoolSize時(shí)Executor默認(rèn)會(huì)先把任務(wù)添加進(jìn)隊(duì)列中,如果無(wú)法加入隊(duì)列則創(chuàng)建新線程直到達(dá)到maximumPoolSize硫麻。
b).maximumPoolSize使用場(chǎng)景爸邢,如果線程池里的線程數(shù)量達(dá)到了maximumPoolSize且其中的線程沒(méi)有空閑的。當(dāng)新任務(wù)到達(dá)的時(shí)候會(huì)新建線程拿愧,如果無(wú)限地創(chuàng)建會(huì)消耗系統(tǒng)的資源杠河,所以這里有一個(gè)maximumPoolSize參數(shù),當(dāng)線程數(shù)量達(dá)到maximumPoolSize的時(shí)候即時(shí)沒(méi)有空閑線程了也不會(huì)重新創(chuàng)建線程浇辜。

不重新創(chuàng)建線程那怎么辦呢券敌?這就需要使用RejectedExecutionHandler(飽和策略)。現(xiàn)有的飽和策略有,策略分兩種執(zhí)行與不執(zhí)行:
對(duì)于不執(zhí)行的柳洋,我可能會(huì)有以下情況:a.丟棄 b.拋出異常 c.丟棄但是記錄日志或持久化到數(shù)據(jù)庫(kù)(通過(guò)實(shí)現(xiàn)RejectedExecutionHandler接口來(lái)處理)待诅。

對(duì)于執(zhí)行該任務(wù)會(huì)有如下的情況:a.騰出空間,替換最老未執(zhí)行的任務(wù)熊镣。
1).丟棄該任務(wù) 2).丟棄最老未執(zhí)行的騰出空間執(zhí)行該任務(wù)

加入隊(duì)列的幾種情況

當(dāng)我們創(chuàng)建線程池需要指定隊(duì)列的時(shí)候必須卑雁,而不同隊(duì)列線程池會(huì)有不同的表現(xiàn)。
有3種常見(jiàn)的隊(duì)列:
a).ArrayBlockingQueue 有界隊(duì)列绪囱,創(chuàng)建時(shí)候必須制定大小(構(gòu)造函數(shù)要求制定)
b).LinkedBlockingQueue 無(wú)界隊(duì)列

 public LinkedBlockingQueue() {
 }

c).SynchronousQueue 同步隊(duì)列测蹲,每新增一個(gè)任務(wù)的線程必須等待另一個(gè)線程取出任務(wù)。 //還是不是很理解同步隊(duì)列怎么實(shí)現(xiàn)的背后的實(shí)現(xiàn)原理-怎么做到同步的鬼吵。
這3種隊(duì)列的使用場(chǎng)景是什么扣甲?
當(dāng)資源有限的時(shí)候使用有界隊(duì)列,使用有界隊(duì)列的過(guò)程中齿椅,隊(duì)列大小和最大池大小可能需要互相折衷琉挖。大隊(duì)列小線程池大小可以降低CPU使用率和線程之間的切換启泣。
使用無(wú)界隊(duì)列時(shí)候maxSize參數(shù)無(wú)用,因?yàn)楫?dāng)線程數(shù)超過(guò)coresize的時(shí)候會(huì)一直不停的往LinkedBlockingQueue里放示辈。這個(gè)可以用于web服務(wù)器訪問(wèn)量突發(fā)的情況寥茫。

線程池如何處理任務(wù)

這里講線程池如何提交任務(wù),任務(wù)提交后如何跟蹤結(jié)果顽耳。
execute方式提交坠敷,這里沒(méi)有返回結(jié)果妙同。所以無(wú)法獲取任務(wù)執(zhí)行結(jié)果射富。

public void execute(Runnable command) {
    ......
    addWorke(command,true)
    }

private boolean addWorker(Runnable firstTask, boolean core) {
     w = new Worker(firstTask); //這里會(huì)把Runable接口包裝城worker接口
     works.add(w);
}

*addWorker 怎么判斷線程池已經(jīng)滿了涉及到二進(jìn)制操作,以后專門寫博客來(lái)闡述粥帚。

submit()方式提交可以通過(guò)future獲取任務(wù)執(zhí)行結(jié)果胰耗,當(dāng)調(diào)用future.get()時(shí)候如果任務(wù)執(zhí)行未完成則會(huì)阻塞。

<T> Future<T> submit(Callable<T> task);

線程池如何關(guān)閉

線程池關(guān)閉的時(shí)候需要考慮其所處的狀態(tài)芒涡,即如果有任務(wù)未執(zhí)行完怎么辦柴灯?什么時(shí)候應(yīng)該關(guān)閉線程池。
常見(jiàn)的辦法就是一個(gè)個(gè)遍歷線程费尽,如果不等待執(zhí)行完就sotp停止線程或者中斷現(xiàn)在執(zhí)行的線程赠群。
線程池關(guān)閉的狀態(tài)中有幾個(gè)中間狀態(tài)可以根據(jù) 隊(duì)列只否有正在執(zhí)行的線程,有的話是否繼續(xù)執(zhí)行來(lái)劃分旱幼。
線程池的狀態(tài)有:Running 可以接收新的任務(wù)和執(zhí)行隊(duì)列任務(wù),shutdown 不接收新的任務(wù)和已有隊(duì)列任務(wù)還需要執(zhí)行,stop 不接收新任務(wù)且 已有隊(duì)列任務(wù)也停止(interrupt in-process task),terminate 線程池已經(jīng)停止了
這里shutdown()與shutdownNow的區(qū)別就是shutdown只會(huì)interrputIdleWork查描,即只會(huì)終端沒(méi)有非運(yùn)行時(shí)的線程,正在執(zhí)行的線程等待執(zhí)行完柏卤。
代碼區(qū)別如下:

 public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(STOP);
            interruptWorkers();
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }
public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(SHUTDOWN);
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
    }

使用篇

前面介紹了線程池的基本功能冬三,這里就對(duì)其如何使用進(jìn)行分析。
使用涉及到配置缘缚、啟動(dòng)勾笆、狀態(tài)監(jiān)控

線程池的配置選型

即對(duì)各種類型的任務(wù)使用什么樣的策略。我暫時(shí)想到的任務(wù)類型劃分標(biāo)準(zhǔn)有桥滨,1.執(zhí)行時(shí)間長(zhǎng)短 2.優(yōu)先級(jí) 3.cpu型的還是IO型的 4.任務(wù)是否依賴其他特性

原則是對(duì)于CPU秘籍型的任務(wù)窝爪,線程池內(nèi)的線程不宜過(guò)多避免頻繁切換,可以設(shè)置為 N cpu+1
對(duì)于IO密集型的任務(wù)齐媒,線程池內(nèi)的線程可以設(shè)置為2*N cpu

對(duì)于優(yōu)先級(jí)可以使用PriorityBlockingQueue隊(duì)列酸舍,但是如果一直有高優(yōu)先級(jí)的任務(wù)那么低優(yōu)先級(jí)的任務(wù)永遠(yuǎn)執(zhí)行不了。

對(duì)于執(zhí)行時(shí)間過(guò)長(zhǎng)的(比如數(shù)據(jù)庫(kù))需要一定時(shí)間才能返回所以空閑時(shí)間比較長(zhǎng)里初,這樣的話可以把線程數(shù)量設(shè)置大一些啃勉。

線程池的監(jiān)控

我們想監(jiān)控線程池所有線程是否執(zhí)行完,線程池里的線程使用狀態(tài)双妨,已經(jīng)完成的線程數(shù)淮阐。
ThreadPoolExecutor提供了一些變量來(lái)存儲(chǔ)線程池的狀態(tài)叮阅,比如taskCount(線程池),completedTaskCount,largestPoolSize(曾經(jīng)創(chuàng)建過(guò)的最大線程數(shù))泣特。

public class ThreadPoolExecutor extends AbstractExecutorService {
       private int largestPoolSize;
       private long completedTaskCount;
}

寫完后的想法

1.通過(guò)從線程池的維度主動(dòng)檢索其知識(shí)來(lái)理解execute(),submit()方法浩姥,這種方式層次清晰,在信息維度就是從高緯往低維度去找是一種好的方式状您。
2.任何框架的描述都可以自己提出很多的問(wèn)題勒叠, 原理、結(jié)構(gòu)膏孟、如何使用等等眯分,通過(guò)這些提問(wèn)來(lái)掌握知識(shí)是一種很好的辦法,從另一角度來(lái)說(shuō)你能夠提出多少問(wèn)題你對(duì)這一領(lǐng)域抽象層次就了解多少柒桑。
3.至于各種線程池還沒(méi)有闡述分析等待下一篇吧弊决。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市魁淳,隨后出現(xiàn)的幾起案子飘诗,更是在濱河造成了極大的恐慌,老刑警劉巖界逛,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昆稿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡息拜,警方通過(guò)查閱死者的電腦和手機(jī)溉潭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)该溯,“玉大人岛抄,你說(shuō)我怎么就攤上這事”奋裕” “怎么了夫椭?”我有些...
    開(kāi)封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)氯庆。 經(jīng)常有香客問(wèn)我蹭秋,道長(zhǎng),這世上最難降的妖魔是什么堤撵? 我笑而不...
    開(kāi)封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任仁讨,我火速辦了婚禮,結(jié)果婚禮上实昨,老公的妹妹穿的比我還像新娘洞豁。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布丈挟。 她就那樣靜靜地躺著刁卜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪曙咽。 梳的紋絲不亂的頭發(fā)上蛔趴,一...
    開(kāi)封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音例朱,去河邊找鬼孝情。 笑死,一個(gè)胖子當(dāng)著我的面吹牛洒嗤,可吹牛的內(nèi)容都是我干的箫荡。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼烁竭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼菲茬!你這毒婦竟也來(lái)了吉挣?” 一聲冷哼從身側(cè)響起派撕,我...
    開(kāi)封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎睬魂,沒(méi)想到半個(gè)月后终吼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡氯哮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年际跪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喉钢。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡姆打,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出肠虽,到底是詐尸還是另有隱情吝秕,我是刑警寧澤法牲,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響供置,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜剥槐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一损谦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧找颓,春花似錦合愈、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)妇汗。三九已至,卻和暖如春说莫,著一層夾襖步出監(jiān)牢的瞬間杨箭,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工储狭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留互婿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓辽狈,卻偏偏與公主長(zhǎng)得像慈参,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子刮萌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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