Java中的Thread Pool
ThreadPool
在Java中想要做非同步程式開發就是建立Thread去執行,但是建立Thread是有成本的,因此如果能將Thread重複利用,則可節省效能,較好的方式就是利用Thread Pool,本文將介紹如何在Java中建立及使用Thread Pool。
ThreadPoolExecutor
在Java1.5後的版本提出的Executor去管控Thread Pool,而ThreadPoolExecutor則可以客製化不同模式的Thread Pool,以下為一建立ThreadPoolExecutor範例
ExecutorService executorService = new ThreadPoolExecutor( //
int corePoolSize, //
int maxPoolSize, //
long keepAliveSeconds, //
TimeUnit unit, //
BlockingQueue<Runnable> workQueue, //
ThreadFactory threadFactory, //
RejectedExecutionHandler handler //
);
- corePoolSize:
Thread的數量,建立的Thread數量不會少於此設定。
- maxPoolSize:
Thread Pool的最大數量,如果所有的Thread都被執行的話,Task會被塞到Queue中,等到有空閒的Thread去執行Task,此數值最好根據系統資源定義出來。
- keepAliveTime:
當閒置時間超過此設定值,系統會開始回收corePoolSize以上多餘的Thread。
- unit:
keepAliveTime的時間單位。
- workQueue:
當所有的Thread都被執行時,Task要用何種方式在Queue中等待。
- threadFactory:
用以創建新的Thread方式,即可依需求客製化Thread,不需要手動在用new Thread()。
- handler:
Queue已滿且Thread已達到maxPoolSize之後會用什麼方式處理新的Task。
BlockingQueue
如上所述,當所有Thread都被執行時,Task會被放入Queue中等待,而其執行方式有一定的規則
- 當前Thread數量小於corePoolSize設定時,則會新增Thread,直到Thread數量大於corePoolSize設定值。
- 當Thread數量大於corePoolSize時,則會將Task放入Queue中等待。
- 當Task無法再被放入Queue中時,則會建立新的Thread至超過maxPoolSize為止。
- 當Thread數量超過maxPoolSize時,則Task會被拒絕。
BlockingQueue有以下三種類型
SynchronousQueue
Queue的Size為0,會直接把Task交給Thread,如超過corePoolSize時,則看是否有設定maxPoolSize,如超過maxPoolSize是有可能拒絕Task,因此使用此類型Queue時,建議不要設定maxPoolSize。
LinkedBlockingQueue
Queue的Size無限制,因為是無限制,所以當Task執行時間過長時有可能造成大量Task卡在Queue中,而導致OOM的發生。
ArrayBlockingQueue
Queue的Size是有限制的,Queue的Size必須要跟Thread Pool相互搭配才行,數量大的Queue Size和數量小的Thread Pool Size可有效降低CPU使用率,但會降低QPS,反之,則提高CPU使用率跟提高QPS。
RejectExecutionHandle
當Queue飽和時,可以根據handle做出相對應的處理方式,共有四種處理的方式。
AbortPolicy
使用此種策略是當Queue數量飽和時,會拋出RejectedExecutionException。
DiscardPolicy
使用此種策略是當Queue數量飽和時,不做處理直接拋棄。
DiscardOldestPolicy
使用此種策略是當Queue數量飽和時,將Queue的第一個Task拋棄,並嘗試重新提交Task。
CallerRunsPolicy
使用此種策略是當Queue數量飽和時,直接呼叫原本的主要Thread來執行Task,但這期間主要的Thread則無法提交新的Task,直到Thread Pool有時間把處理中的Task給完成。
總結
透過ThreadPoolExcutor建立出Thread Pool後則可使用以下方式呼叫,開始非同步的工作執行。
executorService.execute(new Runnable() {
public void run() {
System.out.println("start task");
}
})
當整個應用程式可能已經到達終止的時間點時,有可能因為Thread Pool還持續在執行工作而導致JVM無法被停止,因次最好是在程式中主動關閉Thread Pool,可以呼叫由ExecutorSerivce提供的shutdown()方法達到主動停止的效果。