一文搞懂 java 線程池:基礎知識

你好,我是 shengjk1,多年大廠經驗,努力構建 通俗易懂的、好玩的編程語言教程。 歡迎關注!你會有如下收益:

  1. 了解大廠經驗
  2. 擁有和大廠相匹配的技術等

希望看什么,評論或者私信告訴我!

文章目錄

  • 一、前言
  • 二、線程池
    • 2.1 線程池是什么?
    • 2.2 線程池優勢
    • 2.3 線程池的分類
    • 2.4 線程池的使用
      • 2.4.1 ThreadPoolExecutor 創建
        • 2.4.2 向線程池提交任務
      • 2.4.3 線程池關閉
      • 2.4.4 完整代碼樣例
    • 2.5 如何合理的配置線程池
    • 2.6 線程池監控
    • 2.7 其他
  • 三、擴展
  • 四、總結

一、前言

前面我們幾章我們一起聊了java多線程的一些基本知識以及java多線程間的通信。但往往在多線程開發中使用最多的不是這些原生的東西,而是線程池,幾乎所有需要異步或并發執行任務的程序都可以使用線程池。今天我們就一起聊一下java線程池

二、線程池

2.1 線程池是什么?

顧名思義,線程池就是線程的池子,用來管理和復用線程。它可以在應用程序中有效地管理線程的生命周期、調度和執行。
線程池包含一組預先創建的線程,這些線程可以被動態分配給執行任務,從而避免了不斷創建和銷毀線程的開銷。線程池通常用于執行大量的異步任務,提高多線程程序的性能和穩定性。

2.2 線程池優勢

  1. 降低資源消耗:通過重復利用已創建的線程降低線程創建和銷毀造成的消耗。
  2. 提高響應速度:當任務到達時,任務可以不需要等到線程創建就能立即執行。
  3. 提高線程的可管理性:線程是稀缺資源,如果無限制地創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一分配、調優和監控。

2.3 線程池的分類

在 Java 中,線程池主要分為以下幾類:

  1. FixedThreadPool(固定大小線程池):固定大小線程池中包含固定數量的線程,當有任務提交時,線程池中沒有空閑線程時,任務會被放入任務隊列中等待執行。這種線程池適用于需要限制并發線程數量的場景。
  2. CachedThreadPool(緩存線程池):緩存線程池不固定線程數量,可以根據需要自動創建新線程,當線程空閑時間超過設定的時間(默認60s)會被回收,適用于短期異步任務。
  3. SingleThreadPool(單線程池):單線程池只包含一個工作線程,保證所有任務按順序執行。適用于需要保持任務順序執行的場景。
  4. ScheduledThreadPool(定時線程池):這種線程池可以執行定時任務和周期性任務。它繼承自ExecutorService,并通過 ScheduledExecutorService 接口提供定時任務功能。
  5. WorkStealingPool(工作竊取線程池):這是Java 8中引入的一種新類型的線程池,主要用于處理耗時任務。它適用于需要大量并行任務、任務之間沒有依賴關系的情況。
  6. CustomThreadPool(自定義線程池):除了以上內置的線程池類型,開發人員也可以根據實際需求自定義線程池,通過配置線程池的參數來適應特定的業務場景。 這些線程池類型允許開發人員根據不同的場景和需求選擇適合的線程池,從而提高多線程程序的性能和效率。在實際應用中,根據任務的特性和線程池的負載情況選擇適當的線程池類型非常重要。

2.4 線程池的使用

線程池分類中前4個基本上都是通過 ThreadPoolExecutor 來實現的,所以我們以 ThreadPoolExecutor 為例

2.4.1 ThreadPoolExecutor 創建

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

ThreadPoolExecutor 構造函數包含多個參數,每個參數都影響著線程池的行為。下面解釋每個參數的作用以及如何使用:

  1. corePoolSize:核心線程池大小,當提交一個任務到線程池時,線程池會創建一個線程來執行任務,即使其他空閑的核心線程能夠執行新任務也會創建線程,等到需要執行的任務數大于核心線程池大小時就不再創建。如果調用了線程池的prestartAllCoreThreads()方法,線程池會提前創建并啟動所有核心線程
  • 作用:線程池中保持的最小線程數,即使線程是空閑的。
  • 如何使用:這些線程在處理任務時不會被回收,除非設置了 allowCoreThreadTimeOut(true) 來允許核心線程在空閑時被回收。
  1. maximumPoolSize:線程池的最大線程數
  • 作用:線程池允許的最大線程數,包括核心線程和非核心線程。
  • 如何使用:當任務隊列已滿且活動線程數小于最大線程數時,會創建新的線程。
  1. keepAliveTime:線程空閑時間
  • 作用:非核心線程的空閑時間超過這個值就會被回收。
  • 如何使用:指定非核心線程空閑線程的最長存活時間。
  1. unit:時間單位
  • 作用:用于指定 keepAliveTime 的時間單位,可以是 TimeUnit.MILLISECONDS 等。
  • 如何使用:配合 keepAliveTime 使用。
  1. workQueue:任務隊列
  • 作用:用于保存等待執行的任務。 值得注意的是,如果使用了無界的任務隊列這個參數就沒什么效果
  • 如何使用:選擇合適的任務隊列,例如 LinkedBlockingQueueArrayBlockingQueue 等,以滿足你的需求。
  1. threadFactory:線程工廠
  • 作用:用于創建新線程。 用于設置創建線程的工廠,可以通過線程工廠給每個創建出來的線程設置更有意義的名字
  1. handler:拒絕策略(RejectedExecutionHandler)
  • 作用:當隊列和線程池都滿了,說明線程池處于飽和狀態,那么必須采取一種策略處理提交的新任務。這個策略默認情況下是AbortPolicy,表示無法處理新任務時拋出異常
  • 如何使用:可以選擇使用預設的策略,如 ThreadPoolExecutor.AbortPolicy(任務被拒絕時拋出異常)、ThreadPoolExecutor.CallerRunsPolicy(只用調用者所在線程來運行任務)、ThreadPoolExecutor.DiscardPolicy(丟棄隊列里最近的一個任務,并執行當前任務)、DiscardPolicy (不處理,丟棄掉),或者也可以根據應用場景需要來實現RejectedExecutionHandler接口自定義策略。如記錄日志或持久化存儲不能處理的任務。

通過合理設置這些參數,可以根據具體場景和需求創建一個適合的線程池,提高程序的性能和效率。要根據具體的使用情況和需求來調整這些參數,以獲得最佳的線程池性能。

2.4.2 向線程池提交任務

可以使用兩個方法向線程池提交任務,分別為execute()和submit()方法

  1. execute()方法用于提交不需要返回值的任務,所以無法判斷任務是否被線程池執行成功。
executor.execute(() -> {System.out.println(Thread.currentThread().getName() + " is running...");
});
  1. submit()方法用于提交需要返回值的任務。線程池會返回一個future類型的對象,通過這個future對象可以判斷任務是否執行成功,并且可以通過future的get()方法來獲取返回值,get()方法會阻塞當前線程直到任務完成,而使用get(long timeout,TimeUnit unit)方法則會阻塞當前線程一段時間后立即返回,這時候有可能任務沒有執行完。
Future<String> future = executor.submit(() -> {System.out.println("Task is running on thread: " + Thread.currentThread().getName());return "Task Result";
});
try {// 獲取任務的執行結果String result = future.get();System.out.println("Task result: " + result);
} catch (InterruptedException | ExecutionException e) {e.printStackTrace();
}

2.4.3 線程池關閉

可以通過調用線程池的shutdown或shutdownNow方法來關閉線程池。
它們的原理是遍歷線程池中的工作線程,然后逐個調用線程的interrupt方法來中斷線程,所以無法響應中斷的任務可能永遠無法終止。

但是它們存在一定的區別,

  1. shutdownNow將嘗試立即關閉線程池,首先將線程池的狀態設置成STOP,然后嘗試停止所有的正在執行或暫停任務的線程,并返回等待執行任務的列表,
  2. shutdown只是將線程池的狀態設置成SHUTDOWN狀態,然后中斷所有沒有正在執行任務的線程,等待所有正在執行的任務完成。

只要調用了這兩個關閉方法中的任意一個,isShutdown方法就會返回true。當所有的任務都已關閉后,才表示線程池關閉成功,這時調用isTerminaed方法會返回true。至于應該調用哪一種方法來關閉線程池,應該由提交到線程池的任務特性決定,通常調用shutdown方法來關閉線程池,如果任務不一定要執行完,則可以調用shutdownNow方法

2.4.4 完整代碼樣例

public class CustomThreadPoolExecutorExample {public static void main(String[] args) {// 自定義線程工廠ThreadFactory customThreadFactory = new CustomThreadFactory("CustomThread");// 創建 ThreadPoolExecutor,傳入自定義的 ThreadFactoryThreadPoolExecutor executor = new ThreadPoolExecutor(2, // 核心線程數5, // 最大線程數10, // 線程空閑時間TimeUnit.SECONDS, // 時間單位new ArrayBlockingQueue<>(10), // 任務隊列customThreadFactory, // 自定義 ThreadFactorynew ThreadPoolExecutor.AbortPolicy() // 拒絕策略);// 提交任務給線程池// 提交任務并獲取 Future 對象Future<String> future = executor.submit(() -> {System.out.println("Task is running on thread: " + Thread.currentThread().getName());return "Task Result";});try {// 獲取任務的執行結果String result = future.get();System.out.println("Task result: " + result);} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}executor.execute(() -> {System.out.println(Thread.currentThread().getName() + " is running...");});// 關閉線程池executor.shutdown();// 等待線程池關閉try {if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {executor.shutdownNow();if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {System.err.println("線程池無法正常關閉");}}} catch (InterruptedException e) {executor.shutdownNow();Thread.currentThread().interrupt();}}// 自定義線程工廠實現static class CustomThreadFactory implements ThreadFactory {private final String threadNamePrefix;public CustomThreadFactory(String threadNamePrefix) {this.threadNamePrefix = threadNamePrefix;}@Overridepublic Thread newThread(Runnable r) {Thread thread = new Thread(r);thread.setName(threadNamePrefix + "-" + thread.getId());thread.setPriority(Thread.MAX_PRIORITY);return thread;}}
}

2.5 如何合理的配置線程池

根據任務特征來配置線程池是非常重要的,不同類型的任務可能對線程池的需求有所不同。以下是根據任務特征來配置合理線程池的一些建議:

  1. CPU密集型任務: - 對于大量的 CPU 密集型任務,應該配置較小的線程池,以充分利用 CPU 資源,避免線程之間頻繁切換的開銷。 - 可以采用固定大小的線程池,核心線程數設置為處理器數量,避免創建過多的線程。
  2. IO密集型任務: - 對于涉及大量 IO 操作的任務,應該配置較大的線程池,以充分利用線程阻塞時間,提高系統整體的 IO 效率。 - 可以考慮使用帶有隊列的緩沖線程池,避免線程池中的線程過度增長。
  3. 長時間阻塞任務: - 針對長時間阻塞的任務,不宜使用較小的核心線程數,以免影響其他任務的執行。 比如:依賴數據庫連接池的任務,因為線程提交SQL后需要等待數據庫返回結果,等待的時間越長,則CPU空閑時間就越長,那么線程數應該設置得越大,這樣才能更好地利用CPU
  4. 短時任務: - 對于大量短時任務,可以使用緩存線程池,避免頻繁創建和銷毀線程,提高性能。 - 使用具有自動回收機制的線程池,可以根據需求動態調整線程池大小。
  5. 定時任務: - 對于定時任務,應該考慮使用支持定時任務執行的線程池,如 ScheduledThreadPoolExecutor。 - 設置合適的核心線程數和最大線程數,避免因任務過多導致調度延遲。
    總的來說,根據任務特征來配置線程池需要結合任務的特點和系統的實際情況,合理選擇核心線程數、最大線程數、任務隊列類型、存活時間等參數,以提高系統性能、資源利用率和穩定性。不斷監控線程池的運行情況,并根據實際需求及時調整配置,是保證線程池運行效率的關鍵。

2.6 線程池監控

可以通過線程池提供的參數進行監控,在監控線程池的時候可以使用以下屬性。

  • taskCount:線程池需要執行的任務數量。
  • completedTaskCount:線程池在運行過程中已完成的任務數量,小于或等于taskCount。
  • largestPoolSize:線程池里曾經創建過的最大線程數量。通過這個數據可以知道線程池是否曾經滿過。如該數值等于線程池的最大大小,則表示線程池曾經滿過。
  • getPoolSize:線程池的線程數量。如果線程池不銷毀的話,線程池里的線程不會自動銷毀,所以這個大小只增不減。
  • getActiveCount:獲取活動的線程數。
    -擴展線程池進行監控。可以通過繼承線程池來自定義線程池,重寫線程池的beforeExecute、afterExecute和terminated方法,也可以在任務執行前、執行后和線程池關閉前執行一些代碼來進行監控。例如,監控任務的平均執行時間、最大執行時間和最小執行時間等。這幾個方法在線程池里是空方法。

2.7 其他

建議使用有界隊列。有界隊列能增加系統的穩定性和預警能力,可以根據需要設大一點兒

三、擴展

在HotSpot VM的線程模型中,Java線程(java.lang.Thread)被一對一映射為本地操作系統線程。Java線程啟動時會創建一個本地操作系統線程;當該Java線程終止時,這個操作系統線程也會被回收。操作系統會調度所有線程并將它們分配給可用的CPU

四、總結

本文為Java多線程編程提供了全面的線程池指南,從基本概念到具體實現,再到優化配置和監控,幫助讀者深入理解并有效運用線程池,以提升并發編程的性能和效率。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/39375.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/39375.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/39375.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux:網絡基礎1

文章目錄 前言1. 協議1.1 為什么要有協議&#xff1f;1.2 什么是協議&#xff1f; 2. 網絡2.1 網絡通信的問題2.2 網絡的解決方案——網絡的層狀結構2.3 網絡和系統的關系2.4 網絡傳輸基本流程2.5 簡單理解IP地址2.6 跨網絡傳輸 總結 前言 在早期的計算機發展中&#xff0c;一開…

【云計算】阿里云、騰訊云、華為云平臺數據庫對比

目錄 一、云數據庫關鍵信息調研對比 二、詳細功能 1、阿里云RDS 2、騰訊云RDS 3、華為云RDS 一、云數據庫關鍵信息調研對比 云平臺支持數據庫部署對比支持功能備注阿里云 Mysql、Postgresql等 特有數據庫&#xff1a;PolarDB&#xff0c;適配mysql 基礎-單節點賬號管…

實現漸變字體的方案

需要注意&#xff0c;這個切圖是把一整塊&#xff0c;都切出來做的。所以需要用span&#xff0c;不能是div 還有描邊的話&#xff0c;scale會有邊距縮放的問題&#xff0c;描邊就用font weight 來實現 style{{ background: "var(--Linear, linear-gradient(96deg, #fff…

【華為戰報】5月、6月HCIP考試戰報!

華為認證&#xff1a;HCIA-HCIP-HCIE 點擊查看&#xff1a; 【華為戰報】4月 HCIP考試戰報&#xff01; 【華為戰報】2月、3月HCIP考試戰報&#xff01; 【華為戰報】11月份HCIP考試戰報&#xff01; 【HCIE喜報】HCIE備考2個月絲滑通關&#xff0c;考試心得分享&#xff…

Python序列化和反序列化

一.序列化和反序列化 在Python中&#xff0c;序列化&#xff08;Serialization&#xff09;和反序列化&#xff08;Deserialization&#xff09;是處理對象數據的過程&#xff0c;主要用于對象的存儲或網絡傳輸。 序列化&#xff08;Serialization&#xff09; 序列化是將Pyth…

7.x86游戲實戰-C++實現跨進程讀寫-跨進程寫內存

免責聲明&#xff1a;內容僅供學習參考&#xff0c;請合法利用知識&#xff0c;禁止進行違法犯罪活動&#xff01; 本次游戲沒法給 內容參考于&#xff1a;微塵網絡安全 上一個內容&#xff1a;6.x86游戲實戰-C實現跨進程讀寫-通過基址讀取人物狀態標志位 上一個內容通過基…

深層神經網絡

深層神經網絡 深層神經網絡 深度神經網絡&#xff08;Deep Neural Networks&#xff0c;DNN&#xff09;可以理解為有很多隱藏層的神經網絡&#xff0c;又被稱為深度前饋網絡&#xff08;DFN&#xff09;&#xff0c;多層感知機&#xff08;Multi-Layer perceptron&#xff0c…

ghost恢復?電腦文件恢復如何操作?電腦數據恢復工具!5款!

在數字化時代&#xff0c;電腦數據的價值日益凸顯。然而&#xff0c;數據丟失、誤刪、系統崩潰等問題時有發生&#xff0c;給個人和企業帶來巨大損失。本文將為您詳細介紹Ghost恢復方法&#xff0c;同時推薦五款高效的電腦數據恢復工具&#xff0c;助您輕松應對數據丟失的困擾。…

使用歸檔實用工具怎么打不開 mac上好用的解壓軟件 歸檔實用工具打不開怎么回事 mac 歸檔實用工具 蘋果電腦好用的壓縮軟件有哪些

Mac系統自帶的 “歸檔實用工具”&#xff0c;集成在系統右鍵菜單中&#xff0c;包含了文件壓縮和壓縮包解壓功能。很多mac小伙伴會發現有些文件使用歸檔實用工具打不開。由于專利和軟件開源問題&#xff0c;該工具目前僅支持ZIP格式的壓縮和解壓。同時&#xff0c;對于一些在Wi…

深入探討數據結構:基礎理論與應用實踐

前言 數據結構是計算機科學的重要組成部分&#xff0c;是編程與算法設計的基礎。本文將系統地介紹數據結構的基礎概念、常見類型、具體實現及其在實際開發中的應用&#xff0c;幫助讀者深入理解這一核心領域。 一、數據結構的基本概念 數據結構指的是計算機中數據的組織、管…

推廣旅游卡項目,一個月創收十幾萬,為何說旅游卡項目堪稱盈利利器?

推廣旅游卡項目&#xff0c;一月個創收十幾萬&#xff0c;為何說旅游卡項目堪稱盈利利器&#xff1f; 其精髓恰在于那十六字真言&#xff1a;即時收益&#xff0c;高額利潤&#xff0c;操作簡便&#xff0c;粉絲友好。接下來&#xff0c;我將從推廣人員的視角&#xff0c;為您…

Microsoft SQL Server 2019安裝和設置用戶密碼

1、免費下載兩個安裝包 SQL2019-SSEI-Dev 地址:https://www.microsoft.com/en-us/sql-server/sql-server-downloads SSMS-Setup-CHS 地址:https://aka.ms/ssmsfullsetup 安裝具體不在闡述了&#xff0c;可以參考我這篇文章&#xff1a;SQL Server 2019安裝詳細教程 2、以W…

開發常見的http狀態碼.——400,401,403,404,500,501,503,狀態碼大全!

目錄 一. 1開頭的(臨時信息響應碼) 二. 2開頭的(成功信息碼) 三. 3開頭的(重定向信息碼) 四. 4開頭的(客戶端錯誤信息碼) 五. 5開頭的(服務器內部錯誤信息碼) 一. 1開頭的(臨時信息響應碼) 100&#xff1a;繼續請求。示意請求者應當繼續發送請求&#xff0c;客戶端返回此碼…

Cookie的默認存儲路徑以及后端如何設置

問題場景 最近在寫一個前后端分離的項目&#xff0c;需要跨域&#xff0c;前端開發同學遇到一個問題一直報錯&#xff0c;本質上就是后端返回的cookie中的sessionID在前端發送http請求時無法被請求自動攜帶&#xff0c;每次htttpRequest都被后端識別為一個新的session&#xf…

Spring MVC數據綁定和響應——數據回寫(二)JSON數據的回寫

項目中已經導入了Jackson依賴&#xff0c;可以先調用Jackson的JSON轉換的相關方法&#xff0c;將對象或集合轉換成JSON數據&#xff0c;然后通過HttpServletResponse將JSON數據寫入到輸出流中完成回寫&#xff0c;具體步驟如下。 1、修改文件DataController.java&#xff0c;在…

verilog 參數用法

參數比較運算 localparam QPLL_FBDIV_IN (QPLL_FBDIV_TOP 16) ? 10b0000100000 : (QPLL_FBDIV_TOP 20) ? 10b0000110000 :(QPLL_FBDIV_TOP 32) ? 10b0001100000 :(QPLL_FBDIV_TOP 40) ? 10b0010000000 :(QPLL_FBDIV_TOP 64) ? 10b0011100000 :(QPLL_FBDIV_TO…

昇思25天學習打卡營第04天 | 數據集 Dataset

昇思25天學習打卡營第04天 | 數據集 Dataset 文章目錄 昇思25天學習打卡營第04天 | 數據集 Dataset數據集加載數據集迭代數據集的變換shufflemapbatch 自定義數據集可隨機訪問數據集對象可迭代數據集生成器 總結打卡 數據集Dataset對原始數據進行封裝、變換&#xff0c;為神經網…

Linux 靜態庫 和 動態庫

在Linux系統上&#xff0c;庫文件用于共享和重用代碼。根據使用方式和鏈接方式的不同&#xff0c;庫文件可以分為靜態庫和動態庫。 靜態庫&#xff08;Static Library&#xff09; 靜態庫是在編譯時被嵌入到最終可執行文件中的庫。靜態庫的擴展名通常是.a。 特點 獨立性&am…

ADOP帶你了解:SFP 光模塊:構建高速網絡的關鍵技術

在數字化時代&#xff0c;企業運營的效率往往取決于數據傳輸的速度。因此&#xff0c;構建一個可靠的網絡基礎架構至關重要。本指南深入探討了小型可插拔&#xff08;SFP&#xff09;光收發器的關鍵作用&#xff0c;這些設備確保了網絡中數據的高效和安全流動。SFP光收發器的設…

【Rust入門教程】hello world程序

文章目錄 前言Hello World程序運行總結 前言 對于學習任何一種新的編程語言&#xff0c;我們都會從編寫一個簡單的Hello World程序開始。這是一個傳統&#xff0c;也是一個開始。在這篇文章中&#xff0c;我們將一起學習如何在Rust中編寫你的第一個程序&#xff1a;Hello Worl…