線程池【開發實踐】

文章目錄

  • 一、為什么要用線程池
    • 1.1 單線程的問題
    • 1.2 手動創建多線程的問題
    • 1.3 線程池的作用(優點)
    • 1.4 線程池的使用場景
  • 二、線程池的基礎知識
    • 2.1 線程池的核心組件
    • 2.2 JUC中的線程池架構
    • 2.3 線程池的配置參數
    • 2.4 線程池常見的拒絕策略(可自定義)
    • 2.5 線程池的生命周期
    • 2.6 線程池的任務調度規則
    • 2.7 線程池執行器的鉤子方法
  • 三、使用基礎
    • 3.1 常見的工作隊列
    • 3.2 Executors提供的線程池
    • 3.3 使用Executors創建線程的問題
  • 四、開發實踐
    • 4.1 線程數量配置
    • 4.2 提交任務:以submit(callable)為例
    • 4.3 關閉線程池
  • 參考

一、為什么要用線程池

1.1 單線程的問題

單線程無法利用CPU的多核資源,且存在阻塞問題。使用單個線程來處理一組任務,若當前任務阻塞了線程,該線程將失去CPU的使用權,而不是去執行其他可以執行的任務。

1.2 手動創建多線程的問題

需要開發人員去管理線程的生命周期,麻煩,且可能出錯。
還存在并發數不可控的問題,如每次到達一個任務時都新建一個線程來處理,當大量任務到達時,會創建大量的線程,導致線程間的競爭加劇,且可能導致系統資源耗盡。

1.3 線程池的作用(優點)

  • 節省線程創建和銷毀的開銷:線程池通過復用一組線程,可以節省頻繁創建和銷毀線程的開銷。
  • 控制并發數:線程池可以控制最大并發數,避免線程過多導致系統資源耗盡。
  • 提高響應速度:當任務到達時,可以直接使用線程池中已經創建好的線程,節省了新建線程的時間。
  • 管理線程:線程池負責內部線程的生命周期管理,包括創建、銷毀和調度等,無需人工干預。

1.4 線程池的使用場景

  • 處理并發請求:每個請求都要一個線程來處理,使用線程池來復用一組線程,能節省創建和銷毀線程的開銷。
  • 執行異步任務:避免耗時操作(如郵件發送、文件上傳)阻塞主線程,可以使用線程池來異步執行這些任務。
  • 處理IO密集型任務:線程在進行IO操作時會阻塞,使用線程池來調度這些任務,可以在任務阻塞時調度其他任務繼續執行,避免CPU空閑。
  • 處理計算密集型任務:使用線程池來執行計算密集型任務,可以充分利用CPU的多核資源,也能控制并發數量避免系統資源耗盡。
  • 后臺服務:利用線程池來執行后臺服務,與前臺線程的執行獨立,互不影響。
  • 執行定時任務與周期性任務:可以使用ScheduledThreadPool在后臺執行這些任務,與前臺線程的執行獨立。

二、線程池的基礎知識

2.1 線程池的核心組件

  • 線程池管理器:負責線程池的創建、銷毀、管理和配置。
  • 工作線程:工作線程是線程池中的實際工作者,負責執行提交給線程池的任務。
  • 任務接口:每個提交到線程池的任務都需要實現一個任務接口。Runnable用于定義無需返回值的任務,而Callable則用于定義可以有返回值的任務,并且可以拋出異常。
  • 工作隊列(任務隊列):用于暫存等待被執行的任務,有多種實現方式,未必是先進先出。
  • 線程工廠:線程工廠的作用是定義一個規范來創建線程,允許在創建線程時進行定制化設置,例如設置線程的名稱、優先級、是否為守護線程(daemon thread)等屬性。
  • 拒絕策略:見后。
    (后三個核心組件是線程池的后三個配置參數)

2.2 JUC中的線程池架構

Executor
是線程池管理器的頂層接口,只定義了一個方法void execute(Runnable command),用來提交Runnable任務。

ExecutorService
該接口擴展了Executor,增加了更多管理線程池的方法,如提交Callable任務、提交批量任務、線程池的生命周期管理(shutdown()shutdownNow())、獲取線程池狀態等。

AbstractExecutorService實現了該接口中的部分方法,為創建自定義的線程池服務實現提供了基礎框架。

ThreadPoolExecutor繼承自AbstractExecutorService,是JUC中線程池管理器的核心實現類。

Executors是一個靜態工廠類,提供了多種線程池管理器。

ScheduledExecutorService
該接口擴展了ExecutorService,增加了定時執行和周期性執行任務的功能。ScheduledThreadPoolExecutor實現該接口,專為計劃任務而設計。
在這里插入圖片描述

2.3 線程池的配置參數

  • corePoolSize:核心線程數。
  • maximumPoolSize:最大線程數。
  • keepAliveTime:空閑線程存活時間。當線程池中的工作線程數量超過核心線程數時,額外的工作線程在空閑這個時間段后會被終止,直到工作線程數量降到核心線程數。但如果設置了allowCoreThreadTimeOuttrue,那么核心線程也可以被終止。
  • unit:空閑線程存活時間的單位,可以用枚舉類TimeUnit設置。
  • workQueue:工作隊列。
  • threadFactory:線程工廠。
  • handler:拒絕策略。當阻塞隊列已滿且工作線程數大于等于最大線程數時,無法接受新的任務,會按照拒絕策略進行處理。

2.4 線程池常見的拒絕策略(可自定義)

  • AbortPolicy:拒絕策略。是默認策略,會將新任務拒絕,并且拋出RejectedExecutionException異常。
  • DiscardPolicy:拋棄策略。會將新任務丟掉,但不會跑出異常。
  • DiscardOldestPolicy:拋棄最老任務策略。不是丟棄新任務,而是先丟棄最先進入隊列的任務,然后將新任務入隊。
  • CallerRunsPolicy:調用者執行策略。讓提交任務的線程去執行新任務,而不是使用線程池中的線程去執行。
    在這里插入圖片描述

2.5 線程池的生命周期

  • New:新建狀態。當線程池創建后,但尚未開始執行任務時,它處于新建狀態。此時,線程池中的線程尚未啟動。
  • Running:運行狀態。新建的線程池開始接受并處理任務后就會進入運行狀態,這是線程池的正常工作狀態,可以接收新任務和處理工作隊列中的任務。
  • Shutdown:關閉狀態。調用shutdown()方法后,線程池會進入關閉狀態。此時,線程池不再接受新任務,但不會立刻終止,它會繼續處理隊列中已有的任務直到所有任務完成。
  • Stop:停止狀態。調用shutdownNow()方法后,線程池會進入停止狀態,此時會中斷正在執行任務的線程,清空任務隊列,并返回未開始執行的任務列表。
  • Tidying:整理狀態。完成關閉狀態或停止狀態的工作后,線程池會進入整理狀態。此階段,線程池中的線程數量降為0,即將調用terminated()鉤子方法。
  • Terminated:終止狀態。在整理狀態后,線程池調用terminated()方法并進入終止狀態。此時線程池生命周期結束,會釋放所有資源。
    在這里插入圖片描述

2.6 線程池的任務調度規則

當向線程池提交一個任務時:

  1. 若線程池中的工作線程數量小于核心線程數,執行器總是優先創建一個新的工作線程來執行任務,而不是使用一個空閑的工作線程(目的是快速讓線程池中有足夠的活躍線程)。若工作線程數量大于等于核心線程數,則根據工作隊列的情況進行相應的處理。
  2. 若工作隊列未滿,則將任務入隊。若工作隊列已滿,則根據工作線程數量與最大線程數的關系進行相應的處理。
  3. 若工作線程數大于等于最大線程數,則執行拒絕策略。否則,會新建一個非核心線程來立即執行新的任務。
    在這里插入圖片描述

2.7 線程池執行器的鉤子方法

beforeExecute(Thread t, Runnable r): 任務執行之前的鉤子方法。
afterExecute(Runnable r, Throwable t): 任務執行之后的鉤子方法。
terminated(): 線程池終止時的鉤子方法。

三、使用基礎

3.1 常見的工作隊列

工作隊列基于阻塞隊列實現,我們可以實現BlockingQueue來自定義阻塞隊列,也可以使用JUC中提供的阻塞隊列:

  • ArrayBlockingQueue:基于數組的有界阻塞隊列。隊列大小是固定的,在創建時必須指定。由于是基于數組,所以訪問速度快,但可能有擴容受限的問題。
  • LinkedBlockingQueue:基于鏈表的阻塞隊列。如果不指定容量,則默認為Integer.MAX_VALUE。相比ArrayBlockingQueue,插入和刪除操作可能稍微慢一點,但由于鏈表的動態性,它可以更靈活地調整大小。
  • PriorityBlockingQueue:基于最小堆的無界的優先級隊列,元素按照自然排序或提供的比較器進行排序。任務按照優先級順序被處理,而非先進先出。
  • DelayQueue:基于PriorityBlockingQueue的無界阻塞隊列,其中元素只有在延遲期滿后才能被獲取,否則將阻塞等待,常用于實現定時任務和延遲操作。
  • SynchronousQueue:一個特殊的隊列,它沒有內部容量,每個插入操作必須等待另一個線程的對應移除操作,反之亦然。適用于直接的生產者-消費者傳遞,非常適合傳遞性操作。
  • LinkedTransferQueue:一個由鏈表結構組成的無界阻塞隊列,支持公平和非公平模式。它還提供了一個tryTransfer()方法,允許生產者直接將元素轉移給等待中的消費者,如果沒有消費者等待,則可以選擇是否阻塞(生產者可阻塞)。
  • LinkedBlockingDeque:雙向鏈表實現的雙向阻塞隊列,既可以用作棧,也可以用作隊列。它支持有限或無界的隊列,并提供了putFirst()putLast()等方法來控制元素的插入位置。

3.2 Executors提供的線程池

在這里插入圖片描述

3.3 使用Executors創建線程的問題

  • 定長線程池/單線程話線程池:阻塞隊列無界,可能導致JVM出現OOM(Out Of Memory)異常。
  • 定時線程池/可緩存線程池:最大線程數量不設限上,如果任務提交較多,就會造成大量的線程被啟動,可能造成OOM異常,也可能導致CPU線程資源耗盡。

四、開發實踐

4.1 線程數量配置

IO密集型
核心線程數設置得比CPU核心數稍大,因為當線程在等待I/O時,CPU可以調度其他線程執行。最大線程數可以設置得更高,甚至遠大于CPU核心數,具體數值取決于系統的I/O能力及預期的并發水平。一般推薦設置為2 * CPU核心數或更高,但需注意不要設置得過高以免過度消耗系統資源。
計算密集型
核心線程數和最大線程數通常設置為CPU核心數,因為在這種情況下,更多的線程并不能帶來性能提升,反而會因為上下文切換帶來額外開銷。

4.2 提交任務:以submit(callable)為例

	 // 創建線程池ExecutorService executor = Executors.newSingleThreadExecutor();// 創建Callable任務對象Callable<Result> task = () -> {// ...};// 提交任務并獲取Future對象Future<Result> future = executor.submit(task); // 提交任務并獲取Future// 獲取結果try {// get()方法會阻塞,如果任務執行過程中發生了異常,那么此處會拋出該異常Result res = future.get();} catch (InterruptedException e) {// 處理中斷異常...} catch (ExecutionException e) {// 處理執行異常...}// 關閉ExecutorServiceexecutor.shutdown();

4.3 關閉線程池

調用shutdown()shutdownNow()后,當前線程不會等待線程池的關閉,若當前線程結束了,可能導致內存泄露和資源浪費。

若線程池中還有活動的線程,就算主線程結束了,JVM也會因為存在活躍的非守護線程而無法退出,導致應用程序無法正常退出。

awaitTermination()方法用來阻塞當前線程,直到線程池中的所有任務完成執行或者超時,或者當前線程被中斷。可以用于等待線程池中的線程全部執行完后再退出當前線程,線程池中的所有線程在超時前執行完畢,會返回true并恢復當前線程的執行。

ExecutorService executorService = Executors.newFixedThreadPool(10);// ... 提交任務到線程池// 關閉線程池
executorService.shutdown();
try {// 等待一定時間,直到超時或者線程池中的線程全部執行結束if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {// 如果超時,則嘗試強制關閉executorService.shutdownNow();// 等待一定時間,直到超時或者線程池中的線程全部執行結束if (!executorService.awaitTermination(60, TimeUnit.SECONDS))System.err.println("線程池未在規定時間內停止!");}
} catch (InterruptedException ie) {// 強制終止線程池executorService.shutdownNow();// 保持當前線程的中斷狀態Thread.currentThread().interrupt();
}

參考

Java線程池(超詳細)
Java 多線程:徹底搞懂線程池

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

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

相關文章

appium 實戰問題 播放視頻時無法定位到元素

背景 在做UI自動化時&#xff0c;有播放詳情頁的用例&#xff0c;但是發現視頻在播放的時候無法定位到元素或者很慢&#xff0c;了解到appium在動態的頁面實時獲取布局元素導致定位變慢。所以只能將視頻暫停在操作元素&#xff0c;點擊到暫停按鈕又是個問題&#xff0c;通過ad…

昇思25天學習打卡營第21天|LSTM+CRF序列標注

1. 學習內容復盤 概述 序列標注指給定輸入序列&#xff0c;給序列中每個Token進行標注標簽的過程。序列標注問題通常用于從文本中進行信息抽取&#xff0c;包括分詞(Word Segmentation)、詞性標注(Position Tagging)、命名實體識別(Named Entity Recognition, NER)等。以命名…

Spring Boot項目中JPA操作視圖會改變原表嗎?

一直有一種認識就是:使用JPA對視圖操作,不會影響到原表。 直觀的原因就是視圖是一種數據庫中的虛擬表,它由一個或多個表中的數據通過SQL查詢組成。視圖不包含數據本身,而是保存了一條SQL查詢,這條查詢是用來展示數據的。 但是在實際項目種的一個場景顛覆和糾正了這個認識…

匯川伺服 (4)FFT、機械特性、閉環、慣量、剛性、抑制振動

一、參數解釋 二、FFT 三、機械特性分析 四、多級配方與對象字典 對機組網配方 對象字典 五、InoServoShop 主要是用于調試620P620N將壓縮報解壓后不需要安裝就可以直接使用 六、InoDriveWorkShop 主要是調試660 670 810 520 等系列 慣量識別 Etune Stune 慣量比調試 大慣…

Error:sql: expected 1 arguments, got 2

一 背景 在測試一個API接口時&#xff0c;看到日志里面突然拋出一個錯誤&#xff1a;Error:sql: expected 1 arguments, got 2 看了下&#xff0c;對應的表里面是有相關數據的&#xff0c;sql語句放在mysql里面執行也是沒問題&#xff01;那奇了怪了&#xff0c;為啥會產生這樣…

git只列出本地分支

git只列出本地分支 git branch --list git強制刪除本地分支 git branch -D_error: the branch dlx-test is not fully merged. -CSDN博客文章瀏覽閱讀648次。git branch -d 可以通過: git branch 查看所有本地分支及其名字&#xff0c;然后刪除特定分支。git刪除遠程remote分支…

算法之工程化內容(2)—— Git常用命令

目錄 1. git初始化配置 2. 新建倉庫 3. 工作區——>暫存區——>本地倉庫 4. git reset回退版本 5. 查看差異 git diff 6. 刪除文件git rm 7. .gitignore 8. vscode操作git 9. git分支、合并和刪除 10. 解決合并沖突 11. 回退和rebase 12. 添加遠程倉庫 參考鏈接&#xff…

【Go語言】Go語言的占位符

Go語言的占位符 Golang 的字符串占位符在 fmt 包的各種打印函數中使用&#xff0c;如 fmt.Printf、fmt.Sprintf。 變量值與類型的打印 %v: 打印變量的值 %v 會根據變量的類型選擇合適的格式進行打印。對于結構體&#xff0c;%v 會打印出結構體的字段。對于指針類型&#xf…

Linux 網絡--TCP協議收包流程(NAPI機制)

Linux 網絡--TCP協議收包流程&#xff08;NAPI機制&#xff09; 平臺環境簡介&#xff1a;宿主機: ubuntu18.04Linux內核源碼版本: Linux-4.15網卡驅動: Intel e1000 &#xff08;ubuntu 虛擬機默認網卡驅動&#xff09;協議&#xff1a;TCP協議&#xff0c;本文分析收包過程 本…

緩存新境界:Eureka中服務的分布式緩存實現策略

緩存新境界&#xff1a;Eureka中服務的分布式緩存實現策略 引言 在微服務架構中&#xff0c;服務間的通信和數據交換頻繁&#xff0c;引入分布式緩存可以顯著提高系統性能和響應速度。Eureka作為Netflix開源的服務發現框架&#xff0c;雖然本身不提供緩存機制&#xff0c;但可…

【線程狀態-2】

1、線程禮讓 &#xff08;1&#xff09;禮讓線程&#xff0c;讓當前正在執行的線程暫停&#xff0c;但不阻塞 &#xff08;2&#xff09;將線程從運行狀態轉為就緒狀態 &#xff08;3&#xff09;讓cpu重新調度&#xff0c;禮讓不一定成功&#xff01;看cpu心情 package st…

單對以太網:工業4.0時代的通信革命

單對以太網連接器概述 單對以太網&#xff08;Single Pair Ethernet&#xff0c;簡稱SPE&#xff09;是一種新興的以太網技術&#xff0c;它通過一對雙絞線實現數據傳輸&#xff0c;支持PoDL&#xff08;Power over Data Line&#xff09;技術&#xff0c;為終端設備提供電力供…

【AI工具介紹】— webkit簡介

目錄 一、起源與發展 二、核心組件與功能 三、特性與優勢 四、應用與影響 五、結論 一、起源與發展 WebKit起源于蘋果公司&#xff0c;最初是為了開發Safari瀏覽器而創建的。WebKit項目的起源可以追溯到蘋果公司在2001年推出基于Unix的操作系統Mac OS X時&#xff0c;對瀏…

windows JDK11 與JDK1.8自動切換,以及切換后失效的問題

1.windows安裝不同環境的jdk 2.切換jdk 3.切換失敗 原因&#xff1a;這是因為當我們安裝并配置好JDK11之后它會自動生成一個環境變量&#xff08;此變量我們看不到&#xff09;&#xff0c;此環境變量優先級較高&#xff0c;導致我們在切換回JDK8后系統會先讀取到JDK11生成的…

Java項目:基于SSM框架實現的中小型企業財務管理系統【ssm+B/S架構+源碼+數據庫+答辯PPT+開題報告+畢業論文】

一、項目簡介 本項目是一套基于SSM框架實現的中小型企業財務管理系統 包含&#xff1a;項目源碼、數據庫腳本等&#xff0c;該項目附帶全部源碼可作為畢設使用。 項目都經過嚴格調試&#xff0c;eclipse或者idea 確保可以運行&#xff01; 該系統功能完善、界面美觀、操作簡單…

Spark實現電商消費者畫像案例

作者/朱季謙 故事得從這一張圖開始說起—— 可憐的打工人準備下班時&#xff0c;突然收到領導發來的一份電商消費者樣本數據&#xff0c;數據內容是這樣的—— 消費者姓名&#xff5c;年齡&#xff5c;性別&#xff5c;薪資&#xff5c;消費偏好&#xff5c;消費領域&#x…

CentOS 7:停止更新后如何下載軟件?

引言 CentOS 7 是一個廣受歡迎的 Linux 發行版&#xff0c;它為企業和開發者提供了一個穩定、安全、且免費的操作系統環境。然而&#xff0c;隨著時間的推移&#xff0c;CentOS 7 的官方支持已經進入了維護階段&#xff0c;這意味著它將不再收到常規的更新和新功能&#xff0c;…

圖像類別生成數字標簽

類別 COCO 2017數據集分類標簽。coco2017數據集下載。 cls [background, person, bicycle, car, motorcycle, airplane, bus,train, truck, boat, traffic light, fire hydrant,stop sign, parking meter, bench, bird, cat, dog,horse, sheep, cow, elephant, bear, zebra,…

2024建博會|博聯AI大模型全屋智能引領智能體驗新紀元

7月8日&#xff0c;2024中國建博會&#xff08;廣州&#xff09;在廣交會展館及保利世貿博覽館盛大啟幕。BroadLink博聯智能攜AI大模型全屋智能以及AI商業照明解決方案驚喜亮相&#xff0c;全方位展示AI大模型在智能家居領域的前沿應用成果。 本次建博會&#xff0c;博聯智能帶…

java.lang.annotation包介紹

java.lang.annotation 包是 Java 標準庫中的一個核心包,專門用于定義和支持 Java 注解(Annotation)。該包中包含了一些核心的接口和枚舉類型,用于定義和控制注解在 Java 程序中的行為和使用方式。 主要的類和接口 Annotation 接口 java.lang.annotation.Annotation 所有注…