線程池參數設置


1 線程池

一、ThreadPoolExecutor的重要參數

    1、corePoolSize:核心線程數
       * 核心線程會一直存活,及時沒有任務需要執行
       * 當線程數小於核心線程數時,即使有線程空閑,線程池也會優先創建新線程處理
       * 設置allowCoreThreadTimeout=true(默認false)時,核心線程會超時關閉
​  
   2、maxPoolSize:最大線程數
       * 當線程數>=corePoolSize,且任務隊列已滿時。線程池會創建新線程來處理任務
       * 當線程數=maxPoolSize,且任務隊列已滿時,線程池會拒絕處理任務而拋出異常

   3keepAliveTime:線程空閑時間
       * 當線程空閑時間達到keepAliveTime時,線程會退出,直到線程數量=corePoolSize
       * 如果allowCoreThreadTimeout=true,則會直到線程數量=0
  
   4、allowCoreThreadTimeout:允許核心線程超時
 5、queueCapacity:任務隊列容量(阻塞隊列)
       * 當核心線程數達到最大時,新任務會放在隊列中排隊等待執行
    6、rejectedExecutionHandler:任務拒絕處理器
       * 兩種情況會拒絕處理任務:
           - 當線程數已經達到maxPoolSize,切隊列已滿,會拒絕新任務
           - 當線程池被調用shutdown()后,會等待線程池里的任務執行完畢,再shutdown。如果在調用shutdown()和線程池真正shutdown之間提交任務,會拒絕新任務
       * 線程池會調用rejectedExecutionHandler來處理這個任務。如果沒有設置默認是AbortPolicy,會拋出異常
       * ThreadPoolExecutor類有幾個內部實現類來處理這類情況:
           - AbortPolicy 丟棄任務,拋運行時異常
           - CallerRunsPolicy 執行任務
           - DiscardPolicy 忽視,什么都不會發生
           - DiscardOldestPolicy 從隊列中踢出最先進入隊列(最后一個執行)的任務
       * 實現RejectedExecutionHandler接口,可自定義處理器

二、ThreadPoolExecutor執行順序

      線程池按以下行為執行任務
   1. 當線程數小於核心線程數時,創建線程。
   2. 當線程數大於等於核心線程數,且任務隊列未滿時,將任務放入任務隊列。
   3. 當線程數大於等於核心線程數,且任務隊列已滿
       - 若線程數小於最大線程數,創建線程
       - 若線程數等於最大線程數,拋出異常,拒絕任務

三、如何設置參數

計算公式分為:
CPU密集型(CPU-bound)
線程數一般設置為:
線程數 = CPU核數+1 (現代CPU支持超線程)

IO密集型(I/O bound)
線程數一般設置為:
線程數 = ((線程等待時間+線程CPU時間)/線程CPU時間 * CPU數目
   
1、默認值
       * corePoolSize=1
       * maxPoolSize=Integer.MAX_VALUE
       * keepAliveTime=60s
       * allowCoreThreadTimeout=false
  * queueCapacity=Integer.MAX_VALUE
* rejectedExecutionHandler=AbortPolicy()
* 需要根據幾個值來決定
            - tasks :每秒的任務數
           - tasktime:每個任務花費時間
           - responsetime:系統允許容忍的最大響應時間,比如每個任務的響應時間不得超過2秒。
2、默認值
corePoolSize:
每個任務需要tasktime秒處理,則每個線程每鈔可處理1/tasktime個任務。系統每秒有tasks個任務需要處理,則需要的線程數為:tasks/(1/tasktime),即tasks*tasktime個線程數。假設系統每秒任務數為100~1000,每個任務耗時0.1秒,則需要100*0.1至1000*0.1,即10~100個線程。那么corePoolSize應該設置為大於10,具體數字最好根據8020原則,即80%情況下系統每秒任務數,若系統80%的情況下第秒任務數小於200,最多時為1000,則corePoolSize可設置為20。

queueCapacity:
任務隊列的長度要根據核心線程數,以及系統對任務響應時間的要求有關。隊列長度可以設置為(corePoolSize/tasktime)*responsetime: (20/0.1)*2=400,即隊列長度可設置為400。
隊列長度設置過大,會導致任務響應時間過長,切忌以下寫法:
LinkedBlockingQueue queue = new LinkedBlockingQueue();
這實際上是將隊列長度設置為Integer.MAX_VALUE,將會導致線程數量永遠為corePoolSize,再也不會增加,當任務數量陡增時,任務響應時間也將隨之陡增。

關於網上說這個公式有問題,應該是(corePoolSize/tasktime)*responsetime-corePoolSize。400-20=380,不然隊列的最后20個會超時。(PS:還沒有測試過)

   
maxPoolSize:
當系統負載達到最大值時,核心線程數已無法按時處理完所有任務,這時就需要增加線程。每秒200個任務需要20個線程,那么當每秒達到1000個任務時,則需要(1000-queueCapacity)*(20/200),即60個線程,可將maxPoolSize設置為60。

keepAliveTime:
線程數量只增加不減少也不行。當負載降低時,可減少線程數量,如果一個線程空閑時間達到keepAliveTiime,該線程就退出。默認情況下線程池最少會保持corePoolSize個線程。
   
allowCoreThreadTimeout:
默認情況下核心線程不會退出,可通過將該參數設置為true,讓核心線程也退出。
   
   
   
以上關於線程數量的計算並沒有考慮CPU的情況。若結合CPU的情況,比如,當線程數量達到50時,CPU達到100%,則將maxPoolSize設置為60也不合適,此時若系統負載長時間維持在每秒1000個任務,則超出線程池處理能力,應設法降低每個任務的處理時間(tasktime)。    

總結基於實踐有以下幾個配置公式:
1、corePoolSize = 每秒需要多少個線程處理=tasks/(1/tasktime) =tasks*tasktime
2、queueCapacity = (coreSizePool/tasktime)*responsetime
3、maxPoolSize = (max(tasks)- queueCapacity)/(1/tasktime)

附:

關於queueCapacity任務隊列java中提供了四種方式:直接提交隊列、有界任務隊列、無界任務隊列、優先任務隊列

1.SynchronousQueue

使用SynchronousQueue隊列,提交的任務不會被保存,總是會馬上提交執行。如果用於執行任務的線程數量小於maximumPoolSize,則嘗試創建新的進程,如果達到maximumPoolSize設置的最大值,則根據你設置的handler執行拒絕策略。因此這種方式你提交的任務不會被緩存起來,而是會被馬上執行,在這種情況下,你需要對你程序的並發量有個准確的評估,才能設置合適的maximumPoolSize數量,否則很容易就會執行拒絕策略;

2.ArrayBlockingQueue

使用ArrayBlockingQueue有界任務隊列,若有新的任務需要執行時,線程池會創建新的線程,直到創建的線程數量達到corePoolSize時,則會將新的任務加入到等待隊列中。若等待隊列已滿,即超過ArrayBlockingQueue初始化的容量,則繼續創建線程,直到線程數量達到maximumPoolSize設置的最大線程數量,若大於maximumPoolSize,則執行拒絕策略。在這種情況下,線程數量的上限與有界任務隊列的狀態有直接關系,如果有界隊列初始容量較大或者沒有達到超負荷的狀態,線程數將一直維持在corePoolSize以下,反之當任務隊列已滿時,則會以maximumPoolSize為最大線程數上限。

3.LinkedBlockingQueue

使用無界任務隊列,線程池的任務隊列可以無限制的添加新的任務,而線程池創建的最大線程數量就是你corePoolSize設置的數量,也就是說在這種情況下maximumPoolSize這個參數是無效的,哪怕你的任務隊列中緩存了很多未執行的任務,當線程池的線程數達到corePoolSize后,就不會再增加了;若后續有新的任務加入,則直接進入隊列等待,當使用這種任務隊列模式時,一定要注意你任務提交與處理之間的協調與控制,不然會出現隊列中的任務由於無法及時處理導致一直增長,直到最后資源耗盡的問題。

4.PriorityBlockingQueue

PriorityBlockingQueue它其實是一個特殊的無界隊列,它其中無論添加了多少個任務,線程池創建的線程數也不會超過corePoolSize的數量,只不過其他隊列一般是按照先進先出的規則處理任務,而PriorityBlockingQueue隊列可以自定義規則根據任務的優先級順序先后執行。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM