Executors建立執行緒池原理
Executors建立執行緒池原理
1、ExecutorService介面
- 通過構造引數建立
- 通過Executors建立
實現類ThreadPoolExecutor構造引數為:
//全參建構函式 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
- corePoolSize:核心執行緒數
- maximumPoolSize:最大執行緒數
- keepAliveTime:超過核心執行緒數的執行緒空閒時間
- unit:空閒時間的單位
- workQueue:工作佇列
- threadFactory:建立執行緒的工廠
- handler:任務超出佇列後的處理策略
2、執行緒池的工作流程
(1)執行新的任務,如果執行緒數還沒有超過核心執行緒數,就建立執行緒執行任務
(2)如果執行緒數已經超過核心執行緒數,就將任務放入工作佇列
(3)如果執行緒數已經超過核心執行緒數且工作佇列已滿,檢視執行緒數是否超過最大執行緒數,如果沒有,就建立新的執行緒,將工作佇列中的任務放入執行緒中執行。
(4)如果達到最大執行緒數,就將新的任務交給處理策略進行處理
(5)當核心執行緒數可以處理所有的任務時,超過核心執行緒數的執行緒空閒時間超過keepAliveTime後,就銷燬執行緒。
3、Executors常用的執行緒池
(1)newFixedThreadPool
單引數,指的是執行緒數,指定核心執行緒數和最大執行緒數,都通過該引數指定
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
- 核心執行緒數和最大執行緒數使用同樣的數量
- 超過核心執行緒數的執行緒空閒時間設定為0(不會超過)
- 工作佇列使用的是LinkedBlockingQueue,無界阻塞佇列,該佇列可以無限制的放入任務
(2)newSingleThreadExecutor
預設無參,也可傳入ThreadFactory自定義建立執行緒的工廠
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- 建立核心執行緒數是一個執行緒的執行緒池
- 超時時間為0
- 無界阻塞佇列
(3)newCachedThreadPool
預設無參,也可傳入ThreadFactory自定義建立執行緒的工廠
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
- 建立核心執行緒數為0,最大執行緒數為Integer最大值
- 當池中的執行緒空閒時間超過60秒,銷燬執行緒
- SynchronousQueue工作佇列,屬於同步移交佇列,不是一個真正的佇列,要放入這個佇列,必須要有執行緒等待接收任務,才會放入佇列,並移交給執行緒
(4)newScheduledThreadPool
單個引數傳入核心執行緒數
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
該執行緒池,支援定時以及週期性的執行任務。
ScheduledThreadPoolExecutor構造引數:
傳入父類的引數中:核心執行緒數為引數,最大執行緒數為Integer最大值,超過核心執行緒數的執行緒空閒時間0,阻塞佇列使用DelayedWorkQueue延遲工作佇列。
其父類中使用如下引數,除去傳遞的引數,Factory使用預設的執行緒工廠,飽和策略使用預設的策略defaultHandler=AbortPolicy,當任務超出佇列後丟擲異常。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
4、飽和策略
當核心執行緒數已滿,任務佇列已滿且達到了最大執行緒數時。
(1)AbortPolicy:丟擲異常,讓使用者自己捕獲異常進行處理
(2)DiscardPolicy:什麼也不做,不拋異常
(3)DiscardOldestPolicy:丟棄佇列中隊首的任務,處理新的任務,這種情況下,如果佇列使用的時優先順序佇列,會丟擲優先順序最高的任務,不建議將該策略與優先順序佇列一同使用。
(4)CallerRunsPolicy: 既不拋棄任務也不丟擲異常,直接執行任務的run方法,將任務回退給呼叫者來直接執行。使用該策略時執行緒池飽和後將由呼叫執行緒池的主執行緒自己來執行任務,因此在執行任務的這段時間裡主執行緒無法再提交新任務,從而使執行緒池中工作執行緒有時間將正在處理的任務處理完成。
5、任務佇列型別
-
無界阻塞佇列:沒有大小限制的佇列,常用的無界佇列為 LinkedBlockingQueue ,當某個任務執行比較耗時時,新的任務都會堆積在該佇列中
-
有界阻塞佇列:
常用的有兩類,一類是遵循FIFO原則的佇列如ArrayBlockingQueue與有界的LinkedBlockingQueue,另一類是優先順序佇列如PriorityBlockingQueue。PriorityBlockingQueue中的優先順序由任務的Comparator決定。
使用有界佇列時佇列大小需和執行緒池大小互相配合,執行緒池較小有界佇列較大時可減少記憶體消耗,降低cpu使用率和上下文切換,但是可能會限制系統吞吐量。 -
同步移交佇列: 如果不希望任務在佇列中等待而是希望將任務直接移交給工作執行緒,可使用SynchronousQueue作為等待佇列。SynchronousQueue不是一個真正的佇列,而是一種執行緒之間移交的機制。要將一個元素放入SynchronousQueue中,必須有另一個執行緒正在等待接收這個元素。只有在使用無界執行緒池或者有飽和策略時才建議使用該佇列。