1. 程式人生 > >線程池參數詳解

線程池參數詳解

線程池參數 線程池 參數 java

JDK1.5中引入了強大的concurrent包,其中最常用的莫過了線程池的實現ThreadPoolExecutor,它給我們帶來了極大的方便,但同時,對於該線程池不恰當的設置也可能使其效率並不能達到預期的效果,甚至僅相當於或低於單線程的效率。


ThreadPoolExecutor類可設置的參數主要有:


corePoolSize

在創建了線程池後,默認情況下,線程池中並沒有任何線程,而是等待有任務到來才創建線程去執行任務,(除非調用了prestartAllCoreThreads()或者prestartCoreThread()方法,從這2個方法的名字就可以看出,是預創建線程的意思,即在沒有任務到來之前就創建corePoolSize個線程或者一個線程)。


默認情況下,在創建了線程池後,線程池中的線程數為0,當有任務來之後,就會創建一個線程去執行任務,當線程池中的線程數目達到corePoolSize後,就會把到達的任務放到緩存隊列當中。核心線程在allowCoreThreadTimeout被設置為true時會超時退出,默認情況下不會退出。


maxPoolSize

當線程數大於或等於核心線程,且任務隊列已滿時,線程池會創建新的線程,直到線程數量達到maxPoolSize。如果線程數已等於maxPoolSize,且任務隊列已滿,則已超出線程池的處理能力,線程池會拒絕處理任務而拋出異常。

keepAliveTime

當線程空閑時間達到keepAliveTime,該線程會退出,直到線程數量等於corePoolSize。如果allowCoreThreadTimeout設置為true,則所有線程均會退出直到線程數量為0。


allowCoreThreadTimeout

是否允許核心線程空閑退出,默認值為false。


queueCapacity

任務隊列容量。從maxPoolSize的描述上可以看出,任務隊列的容量會影響到線程的變化,因此任務隊列的長度也需要恰當的設置。


還有就是 workQueue:一個阻塞隊列,用來存儲等待執行的任務,這個參數的選擇也很重要,會對線程池的運行過程產生重大影響,一般來說,這裏的阻塞隊列有以下幾種選擇:


ArrayBlockingQueue;

LinkedBlockingQueue;

SynchronousQueue;

PriorityBlockingQueue

ArrayBlockingQueue和PriorityBlockingQueue使用較少,一般使用LinkedBlockingQueue和Synchronous。線程池的排隊策略與BlockingQueue有關。

threadFactory:線程工廠,主要用來創建線程;

handler:表示當拒絕處理任務時的策略,有以下四種取值:

ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。

ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。

ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新嘗試執行任務(重復此過程)

ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務


線程池按以下行為執行任務


當線程數小於核心線程數時,創建線程。

當線程數大於等於核心線程數,且任務隊列未滿時,將任務放入任務隊列。

當線程數大於等於核心線程數,且任務隊列已滿

若線程數小於最大線程數,創建線程

若線程數等於最大線程數,拋出異常,拒絕任務


系統負載


參數的設置跟系統的負載有直接的關系,下面為系統負載的相關參數:


tasks,每秒需要處理的最大任務數量

tasktime,處理第個任務所需要的時間

responsetime,系統允許任務最大的響應時間,比如每個任務的響應時間不得超過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,再也不會增加,當任務數量陡增時,任務響應時間也將隨之陡增。



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)。


線程池參數詳解