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

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

