java執行緒池總結
Executor 框架
2.1 簡介
Executor 框架是 Java5 之後引進的,在 Java 5 之後,通過 Executor 來啟動執行緒比使用 Thread 的 start 方法更好,除了更易管理,效率更好(用執行緒池實現,節約開銷)外,還有關鍵的一點:有助於避免 this 逃逸問題。
補充:this 逃逸是指在建構函式返回之前其他執行緒就持有該物件的引用. 呼叫尚未構造完全的物件的方法可能引發令人疑惑的錯誤。
Executor 框架不僅包括了執行緒池的管理,還提供了執行緒工廠、佇列以及拒絕策略等,Executor 框架讓併發程式設計變得更加簡單。
2.2 Executor 框架結構(主要由三大部分組成)
1) 任務(Runnable
/Callable
)
執行任務需要實現的Runnable
介面或Callable
介面。Runnable
介面或Callable
介面實現類都可以被ThreadPoolExecutor
或ScheduledThreadPoolExecutor
執行。
2) 任務的執行(Executor
)
如下圖所示,包括任務執行機制的核心介面Executor
,以及繼承自Executor
介面的ExecutorService
介面。ThreadPoolExecutor
和ScheduledThreadPoolExecutor
這兩個關鍵類實現了ExecutorService 介面。
這裡提了很多底層的類關係,但是,實際上我們需要更多關注的是ThreadPoolExecutor
注意:通過檢視
ScheduledThreadPoolExecutor
原始碼我們發現ScheduledThreadPoolExecutor
實際上是繼承了ThreadPoolExecutor
並實現了 ScheduledExecutorService ,而ScheduledExecutorService
又實現了ExecutorService
,正如我們下面給出的類關係圖顯示的一樣。
ThreadPoolExecutor
類描述:
//AbstractExecutorService實現了ExecutorService介面 publicclass ThreadPoolExecutor extends AbstractExecutorService
ScheduledThreadPoolExecutor
類描述:
//ScheduledExecutorService實現了ExecutorService介面 public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService
3) 非同步計算的結果(Future
)
Future
介面以及Future
介面的實現類FutureTask
類都可以代表非同步計算的結果。
當我們把Runnable
介面或Callable
介面的實現類提交給ThreadPoolExecutor
或ScheduledThreadPoolExecutor
執行。(呼叫submit()
方法時會返回一個FutureTask
物件)
2.3 Executor 框架的使用示意圖
- 主執行緒首先要建立實現
Runnable
或者Callable
介面的任務物件。 - 把建立完成的實現
Runnable
/Callable
介面的 物件直接交給ExecutorService
執行:ExecutorService.execute(Runnable command)
)或者也可以把Runnable
物件或Callable
物件提交給ExecutorService
執行(ExecutorService.submit(Runnable task)
或ExecutorService.submit(Callable <T> task)
)。 - 如果執行
ExecutorService.submit(…)
,ExecutorService
將返回一個實現Future
介面的物件(我們剛剛也提到過了執行execute()
方法和submit()
方法的區別,submit()
會返回一個FutureTask 物件)。由於 FutureTask
實現了Runnable
,我們也可以建立FutureTask
,然後直接交給ExecutorService
執行。 - 最後,主執行緒可以執行
FutureTask.get()
方法來等待任務執行完成。主執行緒也可以執行FutureTask.cancel(boolean mayInterruptIfRunning)
來取消此任務的執行。
(重要)ThreadPoolExecutor 類簡單介紹
執行緒池實現類ThreadPoolExecutor
是Executor
框架最核心的類。
ThreadPoolExecutor 類分析
ThreadPoolExecutor
類中提供的四個構造方法。我們來看最長的那個,其餘三個都是在這個構造方法的基礎上產生(其他幾個構造方法說白點都是給定某些預設引數的構造方法比如預設制定拒絕策略是什麼),這裡就不貼程式碼講了,比較簡單。
/** * 用給定的初始引數建立一個新的ThreadPoolExecutor。 */ public ThreadPoolExecutor(int corePoolSize,//執行緒池的核心執行緒數量 int maximumPoolSize,//執行緒池的最大執行緒數 long keepAliveTime,//當執行緒數大於核心執行緒數時,多餘的空閒執行緒存活的最長時間 TimeUnit unit,//時間單位 BlockingQueue<Runnable> workQueue,//任務佇列,用來儲存等待執行任務的佇列 ThreadFactory threadFactory,//執行緒工廠,用來建立執行緒,一般預設即可 RejectedExecutionHandler handler//拒絕策略,當提交的任務過多而不能及時處理時,我們可以定製策略來處理任務 ) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
下面這些對建立 非常重要,在後面使用執行緒池的過程中你一定會用到!所以,務必拿著小本本記清楚。
ThreadPoolExecutor
3 個最重要的引數:
corePoolSize
:核心執行緒數執行緒數定義了最小可以同時執行的執行緒數量。maximumPoolSize
:當佇列中存放的任務達到佇列容量的時候,當前可以同時執行的執行緒數量變為最大執行緒數。workQueue
:當新任務來的時候會先判斷當前執行的執行緒數量是否達到核心執行緒數,如果達到的話,信任就會被存放在佇列中。
ThreadPoolExecutor
其他常見引數:
keepAliveTime
:當執行緒池中的執行緒數量大於corePoolSize
的時候,如果這時沒有新的任務提交,核心執行緒外的執行緒不會立即銷燬,而是會等待,直到等待的時間超過了keepAliveTime
才會被回收銷燬;unit
:keepAliveTime
引數的時間單位。threadFactory
:executor 建立新執行緒的時候會用到。handler
:飽和策略。關於飽和策略下面單獨介紹一下。
下面這張圖可以加深你對執行緒池中各個引數的相互關係的理解(圖片來源:《Java 效能調優實戰》):