Java線程池的工作機(jī)制詳解
Java線程池的工作機(jī)制
線程池是一種多線程管理機(jī)制,用于限制和控制并發(fā)線程的數(shù)量,以提升系統(tǒng)性能和資源利用率,降低頻繁創(chuàng)建和銷毀線程的開銷,線程池是 Java 并發(fā)編程中的重要工具之一,廣泛應(yīng)用于高性能、多線程的場景
線程池通過復(fù)用已創(chuàng)建的線程執(zhí)行多個任務(wù),避免線程的頻繁創(chuàng)建和銷毀,線程池可以限制線程數(shù)量,防止大量線程導(dǎo)致的系統(tǒng)資源耗盡,線程池使用隊列管理任務(wù),支持任務(wù)調(diào)度和優(yōu)先級,線程池提供策略處理超出能力范圍的任務(wù)
我們可以使用Java提供的ThreadPoolExecutor
類來創(chuàng)建一個線程池實例,讓我們先來看一下他的構(gòu)造方法:
ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler )
可以看到他的構(gòu)造方法有七個參數(shù)
- corePoolSize:核心線程數(shù),線程池中始終保持的線程數(shù)量
- maximumPoolSize:最大線程數(shù),線程池中允許的最大線程數(shù)量
- keepAliveTime:空閑線程的存活時間,超過該時間的空閑線程將被回收
- unit:時間單位,是keepAliveTime的單位
- workQueue:任務(wù)隊列,用于存儲等待執(zhí)行的任務(wù)
- threadFactory:線程工廠,用于創(chuàng)建新線程
- handler:拒絕策略,用于處理超出線程池能力范圍的任務(wù)
那么為什么除了傳入一個核心線程數(shù)之外,還要傳入最大線程數(shù)呢?任務(wù)隊列中又是哪些任務(wù)在排隊等待呢?我們一起來探討一下線程池的工作機(jī)制
假設(shè)創(chuàng)建一個核心線程為2,最大線程數(shù)為4的線程池:
ThreadPoolExecutor pool = new ThreadPoolExecutor( 2, // 核心線程數(shù)量 4, // 最大線程數(shù)量 60, // 空閑線程最大存活時間 TimeUnit.SECONDS, // 單位 new ArrayBlockingQueue<>(2), //創(chuàng)建任務(wù)隊列 Executors.defaultThreadFactory(), // 創(chuàng)建線程工廠 new ThreadPoolExecutor.AbortPolicy() // 任務(wù)拒絕策略 );
核心線程
如果同一時刻來了兩個任務(wù):任務(wù)1和任務(wù)2,那么自然就會把兩個任務(wù)交給兩個核心線程去執(zhí)行:
等待隊列
如果同一時刻提交了4個新的任務(wù),但是我們定義的線程池只有兩個核心線程用來執(zhí)行任務(wù)1和任務(wù)2,此時核心線程全部繁忙,新任務(wù)會被放入等待隊列,那么任務(wù)3和任務(wù)4就會進(jìn)入等待隊列中等待,隊列有容量限制,我們上面?zhèn)魅氲淖枞犃腥萘渴?:
臨時線程
如果同一時刻提交的任務(wù)非常多,比如提交了6個任務(wù),那么核心線程依舊會執(zhí)行任務(wù)1和任務(wù)2,由于此時核心線程全部繁忙,新任務(wù)會被放入等待隊列,任務(wù)3和任務(wù)4會被放如等待隊列中等待執(zhí)行
但是將任務(wù)5放入等待隊列時,等待隊列已滿,線程池會嘗試創(chuàng)建非核心線程:臨時線程來執(zhí)行新的任務(wù),但是核心線程與臨時線程的總和不得超過最大線程數(shù)量,也就是我們傳入的4,所以此時線程池會創(chuàng)建2個臨時線程來執(zhí)行任務(wù)5和任務(wù)6
臨時線程用于處理高峰期任務(wù),但是臨時線程并不是核心線程,當(dāng)臨時線程處于空閑狀態(tài)超過 keepAliveTime
后,臨時線程就會被銷毀
任務(wù)拒絕
如果任務(wù)數(shù)量超過了線程池最大處理能力(核心線程 + 臨時線程 + 等待隊列),則執(zhí)行拒絕策略,例如同一時間提交了7個新任務(wù),按照上面的運行機(jī)制,任務(wù)1、任務(wù)2、任務(wù)5和任務(wù)6會被線程執(zhí)行,任務(wù)3和任務(wù)4會在等待隊列中等待空閑線程
但是由于核心線程 + 臨時線程 + 等待隊列全部滿員,此時任務(wù)7就會執(zhí)行拒絕策略,共有四種拒絕策略:
任務(wù)拒絕策略 | 說明 |
---|---|
ThreadPoolExecutor.AbortPolicy | 默認(rèn)策略 丟棄任務(wù)并拋出RejectedExecutionException異常 |
ThreadPoolExecutor.DiscardPolicy | 丟棄任務(wù),但是不拋出異常 |
ThreadPoolExecutor.DiscardOldestPolicy | 拋棄等待隊列中等待時間最久的任務(wù),把當(dāng)前任務(wù)加入等待隊列 |
ThreadPoolExecutor.CallerRunsPolicy | 調(diào)用任務(wù)的run()方法繞過線程池直接執(zhí)行 |
這就是線程池的工作機(jī)制,線程池通過任務(wù)復(fù)用和資源管理提升了系統(tǒng)的并發(fā)性能,但使用時需結(jié)合具體場景合理配置參數(shù),掌握線程池機(jī)制并深入理解其應(yīng)用場景,將有效提升 Java 開發(fā)效率和系統(tǒng)穩(wěn)定性
那么通常在我們的項目中,線程池的容量應(yīng)該設(shè)置為多大呢?
線程容量
項目中的線程容量(即線程池的大?。┰O(shè)置需要基于項目的類型來制定,不同的項目設(shè)置不同的容量:
CPU密集型任務(wù)
對于主要依賴CPU計算的任務(wù)(如數(shù)據(jù)處理、圖像渲染),線程數(shù)量建議接近或等于CPU核心數(shù)(包括邏輯核心)。
計算公式:
這樣可以充分利用CPU,但又不會因線程上下文切換過多而導(dǎo)致性能下降。
I/O密集型任務(wù)
對于涉及大量I/O操作的任務(wù)(如文件讀寫、網(wǎng)絡(luò)請求、數(shù)據(jù)庫查詢),線程池可以設(shè)置為CPU核心數(shù)的多倍,因為I/O操作通常會讓線程處于阻塞狀態(tài)。
計算公式:
更多線程可以隱藏I/O等待時間,提升系統(tǒng)吞吐量
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
mybatis中使用oracle關(guān)鍵字出錯的解決方法
這篇文章主要給大家介紹了關(guān)于mybatis中使用oracle關(guān)鍵字出錯的解決方法,文中通過示例代碼將解決的方法介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。2017-08-08IntelliJ IDEA之配置JDK的4種方式(小結(jié))
這篇文章主要介紹了IntelliJ IDEA之配置JDK的4種方式(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10劍指Offer之Java算法習(xí)題精講二叉搜索樹與數(shù)組查找
跟著思路走,之后從簡單題入手,反復(fù)去看,做過之后可能會忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會發(fā)現(xiàn)質(zhì)的變化2022-03-03Spring Boot示例分析講解自動化裝配機(jī)制核心注解
這篇文章主要分析了Spring Boot 自動化裝配機(jī)制核心注解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-07-07