天天看點

關于線程池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:根據具體情況來決定,任務不重要可丢棄,任務重要則要利用一些緩沖機制來處理

keepAliveTime和allowCoreThreadTimeout采用預設通常能滿足

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

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

繼續閱讀