JDK 可以創建使用Executors 創建5種類型的線程池
Executors.newCachedThreadPool();
該方法返回一個可根據實際情況調整線程數量的線程池。線程池的線程數量不確定,但若有空閒線程可以複用,則會優先 使用可複用的線程。 若所有線程均工作,又有新的任務提交,則會創建新的線程處理任務。所有線程在當前安任務執行完畢後,將返回線程池進行復用。
Executors.newFixedThreadPool(nThreads)
該方法創建一個固定數量的線程池。該線程池中的線程數量始終不變。當有一個新的任務提交時,線程池中若有空閒線程,則立即執行,若沒有,則新的任務會被 暫存在一個任務隊列中,待有空閒線程時,便處理在任務隊列中的任務。
Executors.newSingleThreadExecutor()
該方法返回一個只有一個線程的線程池。若多餘一個任務被提交到該線程池,任務會被保存哎ige任務隊列中,待線程空閒,按先入先出的順序執行隊列中的任務。
Executors.newSingleThreadScheduledExecutor()
該方法返回一個ScheduledExecutorService對象,線程池大小爲1。ScheduledExectorService接口在ExecutorService接口之上擴展了在給定時間執行某任務的功能, 如在固定的延時之後執行或者週期性執行某個任務。
Executors.newScheduledThreadPool( corePoolSize)
該方法可以返回固定數量的線程池。
具體代碼實現如下:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
我們看下這個構造函數的源碼及解釋:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize 指定了線程的數量。
maximumPoolSize指定了線程池的最大數量
keepAliveTime 線程池中線程的數量超過了corePoolSize 的空閒線程存活的時間。
unit keepAliveTime的單位
workQueue 任務隊列
threadFactory 線程工廠
handler 拒絕策略,來不及處理任務時,採用怎樣的方式拒絕任務。
下面介紹下 workQueue 和handler
參數workQueue 指被提交但未執行的任務隊列,它是一個BlockingQueue接口的對象,僅用於存放Runnable對象。 根據隊列功能分類,在ThreadPoolExecutor的構造函數中可使用一下幾種BlockingQueue。
直接提交的隊列: 該功能由 SynchronousQueue對象提供。SynchronousQueue 是一個特殊的BlocingQueue。 它沒有容量,每一個插入操作都要等待一個相應的刪除操作,反之,每一個刪除操作都要等待對應的插入操作。如皋市使用SynchronousQueue,提交的任務不會被真實的保存,而總是將新任務提交給線程執行,如果沒有空閒的進程,則嘗試創建新的進程,如果進程的數量已達到最大值,則執行拒絕策略。
有界的任務隊列
有界的任務隊列可以使用ArrayBlockingQueue實現。當使用有界隊列時,若有新的任務需要執行,如果線程池的實際線程數小於corePoolSize,則會優先創建新的線程,若大於corePoolSize,則會將新任務假如等待隊列。若等待隊列已滿,無法加入,在總線程數,不大於maximumPoolSize的前提下,創建新的進程執行任務。若大於maximumPoolSize,則執行拒絕策略。
無界的任務隊列
無界的任務隊列可以通過LinkedBlockingQueue類實現。與有界隊列相反,除非系統資源耗盡,否則無界的任務隊列不存在任務入隊失敗的情況。當有新的任務到來,系統的線程數小於corePoolSize時,線程池會產生新的線程執行任務,但當系統的線程數達到corePoolSize後,就會繼續增加。若後續仍有新的任務假如,而又沒有空閒的線程資源,則任務直接進入對列等待。若任務創建和處理的速度差異很大,無界隊列會保持快速增長,知道耗盡系統內存。
任務優先隊列
優先任務隊列是帶有執行優先級的隊列,它通過PriorityBlockingQueue實現,可以控制任務的只想你個先後順序。它是一個特殊的無界隊列。
ThreadPoolExecutor 核心執行代碼如下:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
這個方法會有三步:
1 如果當前線程的數量小於corePoolSize,則執行addWorker 分配線程執行任務。
2 如果當前的線程數量大於corePoolSize,則將線程放進workQueue隊列中,等待執行。
如果放入失敗,則將任務直接提交,分配線程進行執行。
3 如果當前線程的數量已經達到了maximumPoolSize 則執行拒絕策略。
拒絕策略(下篇繼續)