ThreadPoolExecutor線程池的創建和參數詳解

(文章部分內容轉載自:http://blog.csdn.net/zhouhl_cn/article/details/7392607,向大神致敬!)
最近在做後再後臺管理系統發送郵件的優化的時候需要用到線程池的實現ThreadPoolExecutor,來發送郵件,這也是JDK1.5以來,Java引入了強大的concurrent包,它給我們帶來了極大的方便,但同時,對於該線程池不恰當的設置也可能使其效率並不能達到預期的效果,甚至僅相當於或低於單線程的效率。
JDK中關於ThreadPoolExecutor的源碼:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

在源碼中我們能看到ThreadPoolExecutor中的所有的參數,這些參數是實現線程池高效工作的關鍵:

corePoolSize

核心線程數,核心線程會一直存活,即使沒有任務需要處理。當線程數小於核心線程數時,即使現有的線程空閒,線程池也會優先創建新線程來處理任務,而不是直接交給現有的線程處理。核心線程在allowCoreThreadTimeout被設置爲true時會超時退出,默認情況下不會退出。

maxPoolSize

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

keepAliveTime

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

allowCoreThreadTimeout

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

queueCapacity

任務隊列容量。從maxPoolSize的描述上可以看出,任務隊列的容量會影響到線程的變化,因此任務隊列的長度也需要恰當的設置。
在實際成產中,如何實現ThreadPoolExecutor呢?它又是怎麼執行的呢?

public class PoolSend {
	BlockingQueue<Runnable> workQueue;//任務隊列
	ExecutorService executorService;//線程池接口
	
	//構造線程方法
	public PoolSend(){
		workQueue = new LinkedBlockingQueue<Runnable>();//構造無界的任務隊列,資源足夠,理論可以支持無限個任務。
		executorService = new ThreadPoolExecutor(2, 10, 
                30,TimeUnit.SECONDS, workQueue, 
                new ThreadPoolExecutor.CallerRunsPolicy());
	}
	//將任務放到線程池中
	public void send(Runnable task){
		System.out.println("Pool Send sending mail...");
		executorService.execute(task);
	}
	//關閉線程池
	public void close(){
		executorService.shutdown();
	}
}

查看ThreadPoolExecutor中的execute方法可以知道:
1、當線程數小於核心線程數時,創建線程。
2、當線程數大於等於核心線程數,且任務隊列未滿時,將任務放入任務隊列。
3、當線程數大於等於核心線程數,且任務隊列已滿:
1、若線程數小於最大線程數,創建線程
2、若線程數等於最大線程數,拋出異常,拒絕任務
關於參數的詳細設置如下
corePoolSize:

每個任務需要tasktime秒處理,則每個線程每鈔可處理1/tasktime個任務。系統每秒有tasks個任務需要處理,則需要的線程數爲:tasks/(1/tasktime),即tasks*tasktime個線程數。假設系統每秒任務數爲1001000,每個任務耗時0.1秒,則需要100*0.1至1000*0.1,即10100個線程。那麼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)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章