當前位置:編程學習大全網 - 源碼下載 - 超詳細的線程池使用解析

超詳細的線程池使用解析

Java 中線程池是運用場景最多的並發框架,幾乎所有需要異步或並發執行任務的程序都可以使用線程池。合理的使用線程池可以帶來多個好處:

(1) 降低資源消耗 。通過重復利用已創建的線程降低線程在創建和銷毀時造成的消耗。

(2) 提高響應速度 。當處理執行任務時,任務可以不需要等待線程的創建就能立刻執行。

(3) 提高線程的可管理性 。線程是稀缺資源,如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統壹分配、調優和監控。

線程池的處理流程如上圖所示

線程池中通過 ctl 字段來表示線程池中的當前狀態,主池控制狀態 ctl 是 AtomicInteger 類型,包裝了兩個概念字段:workerCount 和 runState,workerCount 表示有效線程數,runState 表示是否正在運行、正在關閉等狀態。使用 ctl 字段表示兩個概念,ctl 的前 3 位表示線程池狀態,線程池中限制 workerCount 為(2^29 )-1(約 5 億)個線程,而不是 (2^31)-1(20 億)個線程。workerCount 是允許啟動和不允許停止的工作程序的數量。該值可能與實際的活動線程數暫時不同,例如,當 ThreadFactory 在被詢問時未能創建線程時,以及退出線程在終止前仍在執行記時。用戶可見的池大小報告為工作集的當前大小。 runState 提供主要的生命周期控制,取值如下表所示:

runState 隨著時間的推移而改變,在 awaitTermination() 方法中等待的線程將在狀態達到 TERMINATED 時返回。狀態的轉換為:

RUNNING -> SHUTDOWN 在調用 shutdown() 時,可能隱含在 finalize() 中

(RUNNING 或 SHUTDOWN)-> STOP 在調用 shutdownNow() 時

SHUTDOWN -> TIDYING 當隊列和線程池都為空時

STOP -> TIDYING 當線程池為空時

TIDYING -> TERMINATED 當 terminate() 方法完成時

開發人員如果需要在線程池變為 TIDYING 狀態時進行相應的處理,可以通過重載 terminated() 函數來實現。

結合上圖說明線程池 ThreadPoolExecutor 執行流程,使用 execute() 方法提交任務到線程池中執行時分為4種場景:

(1)線程池中運行的線程數量小於 corePoolSize,創建新線程來執行任務。

(2)線程池中運行線程數量不小於 corePoolSize,將任務加入到阻塞隊列 BlockingQueue。

(3)如果無法將任務加入到阻塞隊列(隊列已滿),創建新的線程來處理任務(這裏需要獲取全局鎖)。

(4)當創建新的線程數量使線程池中當前運行線程數量超過 maximumPoolSize,線程池中拒絕任務,調用 RejectedExecutionHandler.rejectedExecution() 方法處理。

源碼分析:

線程池創建線程時,會將線程封裝成工作線程 Worker,Worker 在執行完任務後,還會循環獲取工作隊列裏的任務來執行。

創建線程池之前,首先要知道創建線程池中的核心參數:

corePoolSize (核心線程數大小):當提交任務到線程池時,線程池會創建壹個線程來執行任務,即使其他空閑的基本線程能夠執行新任務也會創建線程,直到需要執行的任務數大於核心線程數時就不再創建。

runnableTaskQueue (任務隊列):用於保存等待執行任務的阻塞隊列。壹般選擇以下幾種:

ArrayBlockingQueue:基於數組的有界阻塞隊列,按照 FIFO 原則對元素進行排序。

LinkedBlockingQueue:基於鏈表的阻塞隊列,按照 FIFO 原則對元素進行排序。

SynchronousQueue:同步阻塞隊列,也是不存儲元素的阻塞隊列。每壹個插入操作必須要等到另壹個 線程調用移除操作,否則插入操作壹直處於阻塞狀態。

PriorityBlockingQueue:優先阻塞隊列,壹個具有優先級的無限阻塞隊列。

maximumPoolSize (最大線程數大小):線程池允許創建的最大線程數,當隊列已滿,並且線程池中的線程數小於最大線程數,則線程池會創建新的線程執行任務。當使用無界隊列時,此參數無用。

RejectedExecutionHandler (拒絕策略):當任務隊列和線程池都滿了,說明線程池處於飽和狀態,那麽必須使用拒絕策略來處理新提交的任務。JDK 內置拒絕策略有以下 4 種:

AbortPolicy:直接拋出異常

CallerRunsPolicy:使用調用者所在的線程來執行任務

DiscardOldestPolicy:丟棄隊列中最近的壹個任務來執行當前任務

DiscardPolicy:直接丟棄不處理

可以根據應用場景來實現 RejectedExecutionHandler 接口自定義處理策略。

keepAliveTime (線程存活時間):線程池的工作線程空閑後,保持存活的時間。

TimeUnit (存活時間單位):可選單位DAYS(天)、HOURS(小時)、MINUTES(分鐘)、MILLISECONDS(毫秒)、MICROSECONDS(微妙)、NANOSECONDS(納秒)。

ThreadFactory (線程工廠):可以通過線程工廠給創建出來的線程設置有意義的名字。

創建線程池主要分為兩大類,第壹種是通過 Executors 工廠類創建線程池,第二種是自定義創建線程池。根據《阿裏java開發手冊》中的規範,線程池不允許使用 Executors 去創建,原因是規避資源耗盡的風險。

創建壹個單線程化的線程池

創建固定線程數的線程池

以上兩種創建線程池方式使用鏈表阻塞隊列來存放任務,實際場景中可能會堆積大量請求導致 OOM

創建可緩存線程池

允許創建的線程數量最大為 Integer.MAX_VALUE,當創建大量線程時會導致 CPU 處於重負載狀態和 OOM 的發生

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

execute() 方法用於提交不需要返回值的任務,所以無法判斷任務是否被線程池執行成功。execute() 方法中傳入的是 Runnable 類的實例。

submit() 方法用於提交需要返回值的任務。線程池會返回壹個 Future 類型的對象,通過 future 對象可以判斷任務是否執行成功,並且可以通過 future 的 get() 方法來獲取返回值。get() 方法會阻塞當前線程直到任務完成,使用 get(long timeout, TimeUnit unit)方法會阻塞當前線程壹段時間後立即返回,這時候可能任務沒有執行完。

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

shutdown() 和 shutdownNow() 方法的區別在於 shutdownNow 方法首先將線程池的狀態設置為 STOP,然後嘗試停止正在執行或暫停任務的線程,並返回等待執行任務的列表,而 shutdown 只是將線程池的狀態設置成 SHUTDOWN 狀態,然後中斷所有沒有正在執行任務的線程。

線程池使用面臨的核心的問題在於: 線程池的參數並不好配置 。壹方面線程池的運行機制不是很好理解,配置合理需要強依賴開發人員的個人經驗和知識;另壹方面,線程池執行的情況和任務類型相關性較大,IO 密集型和 CPU 密集型的任務運行起來的情況差異非常大,這導致業界並沒有壹些成熟的經驗策略幫助開發人員參考。

(1)以任務型為參考的簡單評估:

假設線程池大小的設置(N 為 CPU 的個數)

如果純計算的任務,多線程並不能帶來性能提升,因為 CPU 處理能力是稀缺的資源,相反導致較多的線程切換的花銷,此時建議線程數為 CPU 數量或+1;----為什麽+1?因為可以防止 N 個線程中有壹個線程意外中斷或者退出,CPU 不會空閑等待。

如果是 IO 密集型應用, 則線程池大小設置為 2N+1. 線程數 = CPU 核數 目標 CPU 利用率 (1 + 平均等待時間 / 平均工作時間)

(2)以任務數為參考的理想狀態評估:

1)默認值

2)如何設置 * 需要根據相關值來決定 - tasks :每秒的任務數,假設為500~1000 - taskCost:每個任務花費時間,假設為0.1s - responsetime:系統允許容忍的最大響應時間,假設為1s

以上都為理想值,實際情況下要根據機器性能來決定。如果在未達到最大線程數的情況機器 cpu load 已經滿了,則需要通過升級硬件和優化代碼,降低 taskCost 來處理。

(僅為簡單的理想狀態的評估,可作為線程池參數設置的壹個參考)

與主業務無直接數據依賴的從業務可以使用異步線程池來處理,在項目初始化時創建線程池並交給將從業務中的任務提交給異步線程池執行能夠縮短響應時間。

嚴禁在業務代碼中起線程!!!

當任務需要按照指定順序(FIFO, LIFO, 優先級)執行時,推薦創建使用單線程化的線程池。

本文章主要說明了線程池的執行原理和創建方式以及推薦線程池參數設置和壹般使用場景。在開發中,開發人員需要根據業務來合理的創建和使用線程池達到降低資源消耗,提高響應速度的目的。

原文鏈接:/post/7067324722811240479

  • 上一篇:聊天室源代碼
  • 下一篇:5月打新債
  • copyright 2024編程學習大全網