jdk線程池ThreadPoolExecutor工作原理解析(自己動(dòng)手實(shí)現(xiàn)線程池)(一)線程池介紹在日常開發(fā)中經(jīng)常會(huì)遇到需要使用其它線程將大量任務(wù)異步處理的場(chǎng)景(異步化以及提升系統(tǒng)的吞吐量),而在使用線程的過程中卻存在著兩個(gè)痛點(diǎn) 。
- 在java等很多主流語言中每個(gè)邏輯上的線程底層都對(duì)應(yīng)著一個(gè)系統(tǒng)線程(不考慮虛擬線程的情況) 。操作系統(tǒng)創(chuàng)建一個(gè)新線程是存在一定開銷的,在需要執(zhí)行大量的異步任務(wù)時(shí),如果處理每個(gè)任務(wù)時(shí)都直接向系統(tǒng)申請(qǐng)創(chuàng)建一個(gè)線程來執(zhí)行,并在任務(wù)執(zhí)行完畢后再回收線程,則創(chuàng)建/銷毀大量線程的開銷將無法忍受 。
- 每個(gè)系統(tǒng)線程都會(huì)占用一定的內(nèi)存空間,且系統(tǒng)在調(diào)度不同線程上下文切換時(shí)存在一定的cpu開銷 。因此在一定的硬件條件下,操作系統(tǒng)能同時(shí)維護(hù)的系統(tǒng)線程個(gè)數(shù)相對(duì)而言是比較有限的 。在使用線程的過程中如果沒有控制好流量,會(huì)很容易創(chuàng)建過多的線程而耗盡系統(tǒng)資源,令系統(tǒng)變得不可用 。
池化線程資源池化線程資源,顧名思義就是維護(hù)一個(gè)存活線程的集合(池子) 。提交任務(wù)的用戶程序不直接控制線程的創(chuàng)建和銷毀,不用每次執(zhí)行任務(wù)時(shí)都申請(qǐng)創(chuàng)建一個(gè)新線程,而是通過線程池間接的獲得線程去處理異步任務(wù) 。線程池中的線程在執(zhí)行完任務(wù)后通常也不會(huì)被系統(tǒng)回收掉,而是繼續(xù)待在池子中用于執(zhí)行其它的任務(wù)(執(zhí)行堆積的待執(zhí)行任務(wù)或是等待新任務(wù)) 。線程池通過池化線程資源,避免了系統(tǒng)反復(fù)創(chuàng)建/銷毀線程的開銷,大幅提高了處理大規(guī)模異步任務(wù)時(shí)的性能 。
對(duì)線程資源的申請(qǐng)進(jìn)行收口,限制系統(tǒng)資源的使用如果程序都統(tǒng)一使用線程池來處理異步任務(wù),則線程池內(nèi)部便可以對(duì)系統(tǒng)資源的使用施加一定限制 。例如用戶可以指定一個(gè)線程池最大可維護(hù)的線程數(shù)量,以避免耗盡系統(tǒng)資源 。當(dāng)用戶提交任務(wù)的速率過大,導(dǎo)致線程池中的線程數(shù)到達(dá)指定的最大值時(shí)依然無法滿足需求時(shí),線程池可以通過丟棄部分任務(wù)或限制提交任務(wù)的流量的方式來處理這一問題 。線程池通過對(duì)線程資源的使用進(jìn)行統(tǒng)一收口,用戶可以通過設(shè)置線程池的參數(shù)來控制系統(tǒng)資源的使用,從而避免系統(tǒng)資源耗盡 。
jdk線程池ThreadPoolExecutor簡單介紹前面介紹了線程池的概念,而要深入理解線程池的工作原理最好的辦法便是找到一個(gè)優(yōu)秀的線程池實(shí)現(xiàn)來加以研究 。而自jdk1.5中引入的通用線程池框架ThreadPoolExecutor便是一個(gè)很好的學(xué)習(xí)對(duì)象 。其內(nèi)部實(shí)現(xiàn)不算復(fù)雜,卻在高效實(shí)現(xiàn)核心功能的同時(shí)還提供了較豐富的拓展能力 。
下面從整體上介紹一下jdk通用線程池ThreadPoolExecutor的工作原理(基于jdk8) 。
ThreadPoolExecutor運(yùn)行時(shí)工作流程首先ThreadPoolExecutor允許用戶從兩個(gè)不同維度來控制線程資源的使用,即最大核心線程數(shù)(corePoolSize)和最大線程數(shù)(maximumPoolSize) 。最大核心線程數(shù):核心線程指的是通常常駐線程池的線程 。常駐線程在線程池沒有任務(wù)空閑時(shí)也不會(huì)被銷毀,而是處于idle狀態(tài),這樣在新任務(wù)到來時(shí)就能很快的進(jìn)行響應(yīng) 。最大線程數(shù):和第一節(jié)中提到的一樣,即線程池中所能允許的活躍線程的最大數(shù)量 。
在向ThreadPoolExecutor提交任務(wù)時(shí)(execute方法),會(huì)執(zhí)行一系列的判斷來決定任務(wù)應(yīng)該如何被執(zhí)行(源碼在下一節(jié)中具體分析) 。
- 首先判斷當(dāng)前活躍的線程數(shù)是否小于指定的最大核心線程數(shù)corePoolSize 。如果為真,則說明當(dāng)前線程池還未完成預(yù)熱,核心線程數(shù)不飽和,創(chuàng)建一個(gè)新線程來執(zhí)行該任務(wù) 。如果為假,則說明當(dāng)前線程池已完成預(yù)熱,進(jìn)行下一步判斷 。
經(jīng)驗(yàn)總結(jié)擴(kuò)展閱讀
- 如何保護(hù)環(huán)境
- 哥哥給弟弟的生日祝福句子
- 送給自己的生日快樂句子
- 閨蜜生日快樂的祝福語
- 哪些星座善于偽裝自己 不會(huì)輕易透露隱私
- 廉租房能自己裝修嗎 廉租房申請(qǐng)下來可以不住嗎
- 申請(qǐng)廉租房需要查自己的個(gè)人征信嗎 公租房與廉租房的區(qū)別在哪里
- 廉租房需要搖號(hào)嗎 廉租房需要自己裝修嗎
- 自己手剝核桃仁怎么儲(chǔ)存
- 我的Vue之旅 10 Gin重寫后端、實(shí)現(xiàn)頁面詳情頁 Mysql + Golang + Gin
