1. 程式人生 > >關於執行緒池ThreadPoolExecutor引數設定那些事

關於執行緒池ThreadPoolExecutor引數設定那些事

       由於近期工作需要,最近需要測試開發某元件(該元件中用到了ThreadPoolExecutor)的效能,測試工具是soapUI,由於以前對測試效能方面接觸較少。所以藉此機會再網上查閱了相關資料,總結如下。

     執行緒數到底和什麼有關係?

        其中一種說法,是執行緒數和cpu數有關係。

              總體意思是,當應用是IO密集型時,執行緒數T=2N+1.          

                                    當應用是CPU密集型時,  執行緒數T=N+1

              T=執行緒數   N=cpu邏輯盒數

             IO密集型應用

:系統的CPU效能相對硬碟、記憶體要好很多,大部分的情況是CPU在等I/O (硬碟/記憶體) 的讀/寫操作,此時                                            CPU Loading並不高。

             CPU密集型應用:系統的硬碟、記憶體效能相對CPU要好很多,CPU要讀/寫I/O(硬碟/記憶體),I/O在很短的時間就可以完                                                成,而CPU還有許多運算要處理,CPU Loading很高。

             如果上面對IO和CPU密集型應用解釋不好理解,簡單講如果你的應用大部分是I/O讀寫操作,很少複雜高頻的運算,cpu        大部分是等待I/O運算,那麼你的應用就是IO密集型應用;如果你的應用很少讀寫操作,基本都是高頻且複雜的各種運算,cpu大部分時間都很忙,那麼你的應用是CPU密集型應用。一般情況我們的應用都是I/O密集型應用。

       另外一種說法,是ThreadPoolExecutor執行緒池設定和實際併發量有關係。

      1、先簡單說下執行緒池ThreadPoolExecutor的幾個引數的意思:

      corePoolSize:核心執行緒數

                        核心執行緒會一直存活,及時沒有任務需要執行

                        當執行緒數小於核心執行緒數時,即使有執行緒空閒,執行緒池也會優先建立新執行緒處理

                        設定allowCoreThreadTimeout=true(預設false)時,核心執行緒會超時關閉

    queueCapacity:任務佇列容量(阻塞佇列)

                      當核心執行緒數達到最大時,新任務會放在佇列中排隊等待執行

     maxPoolSize:最大執行緒數

                        當執行緒數>=corePoolSize,且任務佇列已滿時。執行緒池會建立新執行緒來處理任務

                        當執行緒數=maxPoolSize,且任務佇列已滿時,執行緒池會拒絕處理任務而丟擲異常

     keepAliveTime:執行緒空閒時間

                      當執行緒空閒時間達到keepAliveTime時,執行緒會退出,直到執行緒數量=corePoolSize

                       如果allowCoreThreadTimeout=true,則會直到執行緒數量=0

     allowCoreThreadTimeout:為true時允許核心執行緒超時,即核心執行緒空閒時間超時也可以退出

     rejectedExecutionHandler:任務拒絕處理器

 

ThreadPoolExecutor執行順序:

       當執行緒數小於核心執行緒數時,來了新任務就建立新執行緒,即使有空閒執行緒。

       當執行緒數大於等於核心執行緒數,且任務佇列未滿時,將任務放入任務佇列。

       當執行緒數大於等於核心執行緒數,且任務佇列已滿

                   若執行緒數小於最大執行緒數,建立執行緒

                  若執行緒數等於最大執行緒數,丟擲異常,拒絕任務

 

2、再說下如何設定執行緒池的引數,需要根據幾個值來決定

        tasks :每秒的任務併發數,假設為500~1000

        taskcost:每個任務花費時間,假設為0.1s(可以依據soapUI的平均時間avg引數)

        responsetime:系統允許容忍的最大響應時間,假設為1s

     做幾個計算

    corePoolSize = 每秒需要多少個執行緒處理? 

                 threadcount = tasks/(1/taskcost) =tasks*taskcout =  (500~1000)*0.1 = 50~100 個執行緒。corePoolSize設定應該大於50

                根據8020原則,如果80%的每秒任務數小於800,那麼corePoolSize設定為80即可

    queueCapacity = (coreSizePool/taskcost)*responsetime

              計算可得 queueCapacity = 80/0.1*1 = 80。意思是佇列裡的執行緒可以等待1s,超過了的需要新開執行緒來執行

             切記不能設定為Integer.MAX_VALUE,這樣佇列會很大,執行緒數只會保持在corePoolSize大小,當任務陡增時,不能新             開執行緒來執行,響應時間會隨之陡增。

maxPoolSize = (max(tasks)- queueCapacity)/(1/taskcost)

           計算可得 maxPoolSize = (1000-80)/10 = 92

       (最大任務數-佇列容量)/每個執行緒每秒處理能力 = 最大執行緒數

rejectedExecutionHandler:根據具體情況來決定,任務不重要可丟棄,任務重要則要利用一些緩衝機制來處理

keepAliveTimeallowCoreThreadTimeout採用預設通常能滿足

 

總結:到底執行緒數設定根據什麼依據設定,需要各位根據自己機器情況和實際併發量,都測試下,看那個更好選擇哪種設定方案。一般情況,我比較關注三個引數來判斷執行緒池是否配置合理,併發量、avg、tps、cpu使用率。

 假如併發量在50-150之間,我的執行緒執行時間大概是5ms左右,tps在65-200,cpu使用率在30%-70%,這個資料就不是很理想。當然這些測試是在本地筆記本並非伺服器上。