Executor作爲靈活且強大的異步執行框架,其支持多種不同類型的任務執行策略,提供了一種標準的方法將任務的提交過程和執行過程解耦開發,基於生產者-消費者模式,其提交任務的線程相當於生產者,執行任務的線程相當於消費者,並用Runnable來表示任務,Executor的實現還提供了對生命週期的支持,以及統計信息收集,應用程序管理機制和性能監視等機制。
Executor框架主要由3大部分組成如下。
1. 任務。包括被執行任務需要實現的接口:Runnable接口或Callable接口。
2. 任務的執行。包括任務執行機制的核心接口Executor,以及繼承自Executor的ExecutorService接口。Executor框架有兩個關鍵類實現了ExecutorService接口(ThreadPoolExecutor和ScheduledThreadPoolExecutor)。
3. 異步計算的結果。包括接口Future和實現Future接口的FutureTask類。
- Executor是一個接口,他是Executor框架的基礎,它將任務的提交與任務的執行分離。
- ThreadPoolExecutor是線程池的核心實現類,用來執行被提交的任務。
- ScheduledThreadPoolExecutor是一個實現類,可以在給定的延遲後運行命令,或者定期執行命令。ScheduledThreadPoolExecutor 比 Timer 更靈活,功能更強大。
- Future接口和它的實現FutureTask類,代表異步計算的結果。
- Runnable和Callable接口的實現類,都可以被ThreadPoolExecutor 或 ScheduledThreadPoolExecutor 執行。
Executor框架的使用:
- 主線程首先要創建實現 Runnable接口或者Callable接口的任務對象。工具類Executors可以把一個Runnable對象封裝爲一個Callable對象123
Executors.callable(Runnale task);
或
Executors.callable(Runnable task, Object resule);
- 然後可以把Runnable對象直接交給ExecutorService執行123
ExecutorServicel.execute(Runnable command);
或者也可以把Runnable對象或Callable對象提交給ExecutorService執行
ExecutorService.submit(Runnable task);
如果執行ExecutorService.submit(...),ExecutorService將返回一個實現Future接口的對象(到目前爲止的JDK中,返回的是FutureTask對象)。由於FutureTask實現了Runnable接口,我們也可以創建FutureTask類,然後直接交給ExecutorService執行。
execute()方法用於提交不需要返回值的任務,所以無法判斷任務是否被線程池執行成功。
submit()方法用於提交需要返回值的任務。線程池會返回一個future類型的對象,通過這個
future對象可以判斷任務是否執行成功,並且可以通過future的get()方法來獲取返回值,get()方
法會阻塞當前線程直到任務完成,而使用get(long timeout,TimeUnit unit)方法則會阻塞當前線
程一段時間後立即返回,這時候有可能任務沒有執行完。
ThreadPoolExecutor詳解
Executor框架最核心的類是ThreadPoolExecutor
ThreadPoolExecutor的組件構成
- corePool:核心線程池的大小
- maximumPool:最大線程池的大小
- BlockingQueue:用來暫時保存任務的工作隊列
- RejectedExecutionHandler:當ThreadPoolExecutor已經關閉或ThreadPoolExecutor已經飽和時(達到了最大線程池的大小且工作隊列已滿),execute()方法將要調用的Handler。
Executor 可 以 創 建 3 種 類 型 的 ThreadPoolExecutor 線 程 池:
1. FixedThreadPool
創建固定長度的線程池,每次提交任務創建一個線程,直到達到線程池的最大數量,線程池的大小不再變化。
這個線程池可以創建固定線程數的線程池。特點就是可以重用固定數量線程的線程池。它的構造源碼如下:
1 2 3 4 5 | public static ExecutorService newFixedThreadPool( int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } |
- FixedThreadPool的corePoolSize和maxiumPoolSize都被設置爲創建FixedThreadPool時指定的參數nThreads。
- 0L則表示當線程池中的線程數量操作核心線程的數量時,多餘的線程將被立即停止
- 最後一個參數表示FixedThreadPool使用了無界隊列LinkedBlockingQueue作爲線程池的做工隊列,由於是無界的,當線程池的線程數達到corePoolSize後,新任務將在無界隊列中等待,因此線程池的線程數量不會超過corePoolSize,同時maxiumPoolSize也就變成了一個無效的參數,並且運行中的線程池並不會拒絕任務。
FixedThreadPool運行圖如下
執行過程如下:
1.如果當前工作中的線程數量少於corePool的數量,就創建新的線程來執行任務。
2.當線程池的工作中的線程數量達到了corePool,則將任務加入LinkedBlockingQueue。
3.線程執行完1中的任務後會從隊列中去任務。
注意LinkedBlockingQueue是無界隊列,所以可以一直添加新任務到線程池。
2. SingleThreadExecutor
SingleThreadExecutor是使用單個worker線程的Executor。特點是使用單個工作線程執行任務。它的構造源碼如下:
1 2 3 4 5 6 | public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService ( new ThreadPoolExecutor( 1 , 1 , 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } |
執行過程如下:
1.如果當前工作中的線程數量少於corePool的數量,就創建一個新的線程來執行任務。
2.當線程池的工作中的線程數量達到了corePool,則將任務加入LinkedBlockingQueue。
3.線程執行完1中的任務後會從隊列中去任務。
注意:由於在線程池中只有一個工作線程,所以任務可以按照添加順序執行。
3. CachedThreadPool
CachedThreadPool是一個”無限“容量的線程池,它會根據需要創建新線程。特點是可以根據需要來創建新的線程執行任務,沒有特定的corePool。下面是它的構造方法:
1 2 3 4 5 | public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor( 0 , Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } |
1.首先執行SynchronousQueue.offer(Runnable task)。如果在當前的線程池中有空閒的線程正在執行SynchronousQueue.poll(),那麼主線程執行的offer操作與空閒線程執行的poll操作配對成功,主線程把任務交給空閒線程執行。,execute()方法執行成功,否則執行步驟2
2.當線程池爲空(初始maximumPool爲空)或沒有空閒線程時,配對失敗,將沒有線程執行SynchronousQueue.poll操作。這種情況下,線程池會創建一個新的線程執行任務。
3.在創建完新的線程以後,將會執行poll操作。當步驟2的線程執行完成後,將等待60秒,如果此時主線程提交了一個新任務,那麼這個空閒線程將執行新任務,否則被回收。因此長時間不提交任務的CachedThreadPool不會佔用系統資源。
SynchronousQueue是一個不存儲元素阻塞隊列,每次要進行offer操作時必須等待poll操作,否則不能繼續添加元素。
參考文章:Executor框架詳解