Java線程池核心工作原理解析
一、什么是線程池?
核心思想: 線程池是一種基于“池化”思想來(lái)管理線程的工具。它預(yù)先創(chuàng)建好一定數(shù)量的線程,放入一個(gè)“池子”中,當(dāng)有任務(wù)需要執(zhí)行時(shí),就從池子中取出一個(gè)空閑線程來(lái)執(zhí)行任務(wù),任務(wù)執(zhí)行完畢后,線程并不被銷(xiāo)毀,而是返回池中等待執(zhí)行下一個(gè)任務(wù)。
為什么需要線程池?
在深入原理之前,我們先想想如果不使用線程池,我們?nèi)绾翁幚矶嗳蝿?wù):
// 原始方式:為每個(gè)任務(wù)創(chuàng)建一個(gè)新線程
for (int i = 0; i < 100; i++) {
new Thread(() -> {
// 執(zhí)行任務(wù)
System.out.println("執(zhí)行任務(wù):" + Thread.currentThread().getName());
}).start();
}這種方式存在幾個(gè)嚴(yán)重問(wèn)題:
- 資源消耗大:創(chuàng)建和銷(xiāo)毀線程是非常消耗CPU和內(nèi)存的。當(dāng)任務(wù)數(shù)量非常多時(shí),頻繁地創(chuàng)建和銷(xiāo)毀線程會(huì)嚴(yán)重影響性能。
- 管理困難:無(wú)法控制線程的數(shù)量,如果并發(fā)任務(wù)過(guò)多,會(huì)創(chuàng)建大量線程,導(dǎo)致系統(tǒng)負(fù)載過(guò)高,甚至崩潰。
- 穩(wěn)定性差:缺乏統(tǒng)一的管理,線程之間的競(jìng)爭(zhēng)和不可預(yù)知的行為會(huì)增加系統(tǒng)的不穩(wěn)定性。
線程池的優(yōu)勢(shì):
- 降低資源消耗:通過(guò)復(fù)用已創(chuàng)建的線程,避免了頻繁創(chuàng)建和銷(xiāo)毀線程的開(kāi)銷(xiāo)。
- 提高響應(yīng)速度:當(dāng)任務(wù)到達(dá)時(shí),無(wú)需等待線程創(chuàng)建就能立即執(zhí)行。
- 提高線程的可管理性:線程是稀缺資源,線程池可以統(tǒng)一進(jìn)行分配、調(diào)優(yōu)和監(jiān)控??梢钥刂谱畲蟛l(fā)數(shù),防止無(wú)限制創(chuàng)建線程。
- 提供更強(qiáng)大的功能:線程池提供了定時(shí)執(zhí)行、定期執(zhí)行、單線程、并發(fā)數(shù)控制等功能。
二、線程池的核心工作原理
要理解線程池的工作原理,我們需要深入到它的內(nèi)部組件和執(zhí)行流程。其核心模型可以用下圖清晰地展示:

下面我們來(lái)詳細(xì)拆解圖中的每一步。
核心組件
- 核心線程池 (
corePoolSize):線程池中常駐的“核心部隊(duì)”。即使它們處于空閑狀態(tài),也不會(huì)被銷(xiāo)毀(除非設(shè)置了allowCoreThreadTimeOut為true)。 - 任務(wù)隊(duì)列 (
workQueue):一個(gè)阻塞隊(duì)列,用于存放待執(zhí)行的任務(wù)。當(dāng)核心線程都在忙時(shí),新來(lái)的任務(wù)會(huì)被放在這個(gè)隊(duì)列里排隊(duì)等候。 - 最大線程池 (
maximumPoolSize):線程池允許創(chuàng)建的最大線程數(shù)量。這是線程池的“擴(kuò)編上限”。 - 非核心線程:當(dāng)任務(wù)隊(duì)列滿(mǎn)了,并且當(dāng)前線程數(shù)小于最大線程數(shù)時(shí),線程池會(huì)創(chuàng)建新的線程來(lái)處理任務(wù)。這些線程是“臨時(shí)工”,空閑一段時(shí)間后(由
keepAliveTime決定)會(huì)被銷(xiāo)毀。 - 拒絕策略 (
RejectedExecutionHandler):當(dāng)任務(wù)隊(duì)列已滿(mǎn),并且線程數(shù)已達(dá)到最大值時(shí),線程池會(huì)采取一種策略來(lái)處理新提交的任務(wù)。
工作流程詳解(結(jié)合上圖)
- 提交任務(wù)
- 當(dāng)一個(gè)新任務(wù)被提交到線程池時(shí),線程池的處理決策流程開(kāi)始。
- 判斷核心線程
- 如果當(dāng)前運(yùn)行的線程數(shù) 小于
corePoolSize(核心線程數(shù)),那么無(wú)論是否有空閑線程,線程池都會(huì)立即創(chuàng)建一個(gè)新的核心線程來(lái)執(zhí)行這個(gè)任務(wù)。 - 如果核心線程數(shù)已滿(mǎn),則進(jìn)入下一步。
- 如果當(dāng)前運(yùn)行的線程數(shù) 小于
- 嘗試入隊(duì)
- 線程池會(huì)嘗試將任務(wù)放入任務(wù)隊(duì)列 (
workQueue) 進(jìn)行排隊(duì)。 - 如果任務(wù)隊(duì)列未滿(mǎn),任務(wù)成功入隊(duì),等待核心線程空閑下來(lái)后從隊(duì)列中取出執(zhí)行。
- 如果任務(wù)隊(duì)列已滿(mǎn),則進(jìn)入下一步。
- 線程池會(huì)嘗試將任務(wù)放入任務(wù)隊(duì)列 (
- 判斷最大線程
- 如果任務(wù)隊(duì)列已滿(mǎn),但當(dāng)前運(yùn)行的線程數(shù) 小于
maximumPoolSize(最大線程數(shù)),線程池會(huì)創(chuàng)建一個(gè)新的非核心線程來(lái)立即執(zhí)行這個(gè)任務(wù)(注意,它執(zhí)行的是剛提交的這個(gè)新任務(wù),而不是隊(duì)列里的舊任務(wù))。 - 如果當(dāng)前線程數(shù)已經(jīng)達(dá)到
maximumPoolSize,則進(jìn)入下一步。
- 如果任務(wù)隊(duì)列已滿(mǎn),但當(dāng)前運(yùn)行的線程數(shù) 小于
- 執(zhí)行拒絕策略
- 當(dāng)線程池和隊(duì)列都已經(jīng)“滿(mǎn)負(fù)荷”工作時(shí),新提交的任務(wù)將被拒絕,線程池會(huì)調(diào)用
RejectedExecutionHandler來(lái)處理這個(gè)任務(wù)。
- 當(dāng)線程池和隊(duì)列都已經(jīng)“滿(mǎn)負(fù)荷”工作時(shí),新提交的任務(wù)將被拒絕,線程池會(huì)調(diào)用
補(bǔ)充:線程回收
當(dāng)線程池中的線程數(shù)量超過(guò)了corePoolSize,并且這些“多余”的非核心線程空閑時(shí)間超過(guò)了keepAliveTime,它們就會(huì)被終止,直到線程數(shù)量恢復(fù)到corePoolSize的大小。
三、Java中的線程池實(shí)現(xiàn) (ThreadPoolExecutor)
在Java中,線程池的核心類(lèi)是 java.util.concurrent.ThreadPoolExecutor。我們通常通過(guò)Executors工廠類(lèi)來(lái)創(chuàng)建配置好的線程池,但更推薦直接使用ThreadPoolExecutor的構(gòu)造函數(shù)來(lái)精細(xì)控制參數(shù)。
核心構(gòu)造函數(shù)
public ThreadPoolExecutor(
int corePoolSize, // 核心線程數(shù)
int maximumPoolSize, // 最大線程數(shù)
long keepAliveTime, // 非核心線程空閑存活時(shí)間
TimeUnit unit, // 存活時(shí)間單位
BlockingQueue<Runnable> workQueue, // 任務(wù)隊(duì)列
ThreadFactory threadFactory, // 線程工廠(用于創(chuàng)建線程)
RejectedExecutionHandler handler // 拒絕策略
)常見(jiàn)的任務(wù)隊(duì)列 (workQueue)
SynchronousQueue:一個(gè)不存儲(chǔ)元素的隊(duì)列。每個(gè)插入操作必須等待另一個(gè)線程的移除操作。這樣,提交的任務(wù)不會(huì)被排隊(duì),而是直接創(chuàng)建新線程或執(zhí)行拒絕策略。Executors.newCachedThreadPool()使用它。LinkedBlockingQueue:一個(gè)基于鏈表的無(wú)界隊(duì)列(除非構(gòu)造時(shí)指定容量)。如果使用無(wú)界隊(duì)列,那么maximumPoolSize參數(shù)就失效了,因?yàn)殛?duì)列永遠(yuǎn)不會(huì)滿(mǎn),所以只會(huì)創(chuàng)建corePoolSize個(gè)線程。ArrayBlockingQueue:一個(gè)基于數(shù)組的有界隊(duì)列??梢杂行У胤乐官Y源耗盡。
內(nèi)置的拒絕策略
ThreadPoolExecutor.AbortPolicy(默認(rèn)):直接拋出RejectedExecutionException異常。ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用者所在線程(比如主線程)來(lái)執(zhí)行該任務(wù)。這提供了一種簡(jiǎn)單的反饋控制機(jī)制,可以降低新任務(wù)的提交速度。ThreadPoolExecutor.DiscardPolicy:默默丟棄無(wú)法處理的任務(wù),不拋異常。ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊(duì)列中最舊的一個(gè)任務(wù),然后嘗試重新提交當(dāng)前任務(wù)。
四、通過(guò)Executors工具類(lèi)創(chuàng)建的常見(jiàn)線程池
newFixedThreadPool(固定大小線程池)corePoolSize=maximumPoolSize= nworkQueue=LinkedBlockingQueue(無(wú)界隊(duì)列)- 特點(diǎn):線程數(shù)量固定。適用于為了滿(mǎn)足資源管理的需求,需要限制當(dāng)前線程數(shù)量的場(chǎng)景。注意:使用無(wú)界隊(duì)列,如果任務(wù)過(guò)多,可能導(dǎo)致內(nèi)存溢出(OOM)。
newCachedThreadPool(可緩存線程池)corePoolSize= 0maximumPoolSize=Integer.MAX_VALUE(幾乎是無(wú)限的)keepAliveTime= 60秒workQueue=SynchronousQueue- 特點(diǎn):線程數(shù)量幾乎無(wú)限制,空閑線程會(huì)被回收。適用于執(zhí)行很多短期異步任務(wù)的小程序,或負(fù)載較輕的服務(wù)器。注意:最大線程數(shù)非常大,可能創(chuàng)建大量線程,導(dǎo)致CPU和內(nèi)存耗盡。
newSingleThreadExecutor(單線程線程池)corePoolSize=maximumPoolSize= 1workQueue=LinkedBlockingQueue(無(wú)界隊(duì)列)- 特點(diǎn):只有一個(gè)線程工作。適用于需要保證任務(wù)順序執(zhí)行,并且在任意時(shí)間點(diǎn)不會(huì)有多個(gè)線程活動(dòng)的場(chǎng)景。
newScheduledThreadPool(定時(shí)任務(wù)線程池)- 用于在給定的延遲后運(yùn)行任務(wù),或者定期執(zhí)行任務(wù)。
五、最佳實(shí)踐與總結(jié)
- 理解參數(shù):根據(jù)任務(wù)的特性(CPU密集型、IO密集型)合理設(shè)置
corePoolSize、maximumPoolSize和workQueue。- CPU密集型:線程數(shù) ≈ CPU核數(shù) + 1
- IO密集型:線程數(shù)可以設(shè)置得多一些,如 2 * CPU核數(shù)
- 推薦手動(dòng)創(chuàng)建:避免使用
Executors的便捷方法,而是直接使用ThreadPoolExecutor構(gòu)造函數(shù),這樣可以更明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。 - 給線程池命名:通過(guò)自定義
ThreadFactory,為線程設(shè)置有意義的名字,便于出錯(cuò)時(shí)回溯。 - 合理選擇拒絕策略:根據(jù)業(yè)務(wù)重要性選擇合適的拒絕策略。
總結(jié)一下:線程池是一個(gè)“生產(chǎn)者-消費(fèi)者”模型的優(yōu)雅實(shí)現(xiàn)。生產(chǎn)者提交任務(wù)(Runnable對(duì)象),消費(fèi)者(池中的線程)從任務(wù)隊(duì)列中獲取并執(zhí)行任務(wù)。通過(guò)預(yù)先創(chuàng)建和復(fù)用線程,以及對(duì)線程數(shù)量的管理,它極大地提升了多線程程序的性能、穩(wěn)定性和可管理性。理解其核心工作原理是編寫(xiě)高效、健壯并發(fā)程序的關(guān)鍵。
到此這篇關(guān)于Java線程池核心工作原理解析的文章就介紹到這了,更多相關(guān)java線程池內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MapStruct對(duì)象映射轉(zhuǎn)換解決Bean屬性拷貝性能問(wèn)題
無(wú)意間看到項(xiàng)目中有小伙伴用到了 MapStruct 來(lái)做對(duì)象映射轉(zhuǎn)換當(dāng)時(shí)我就很好奇,這個(gè)是什么框架,能夠解決什么問(wèn)題,帶著這兩個(gè)疑問(wèn)就有了下面的文章2022-02-02
Java組件FileUpload上傳文件實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了Java組件FileUpload上傳文件實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-06-06
idea創(chuàng)建spring boot工程及配置文件(最新推薦)
本文給大家介紹idea創(chuàng)建spring boot工程及配置文件,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-11-11
詳解Java后端優(yōu)雅驗(yàn)證參數(shù)合法性
這篇文章主要介紹了詳解Java后端優(yōu)雅驗(yàn)證參數(shù)合法性,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02
Java8中CompletableFuture使用場(chǎng)景與實(shí)現(xiàn)原理
CompletableFuture是java8引入的新類(lèi),該類(lèi)實(shí)現(xiàn)了Future接口和 CompletionStage接口,封裝了future、forkjoin相關(guān)類(lèi)來(lái)執(zhí)行異步,這篇文章主要給大家介紹了關(guān)于Java8中CompletableFuture使用場(chǎng)景與實(shí)現(xiàn)原理的相關(guān)資料,需要的朋友可以參考下2022-02-02

