Executor框架

    Executor作爲靈活且強大的異步執行框架,其支持多種不同類型的任務執行策略,提供了一種標準的方法將任務的提交過程和執行過程解耦開發,基於生產者-消費者模式,其提交任務的線程相當於生產者,執行任務的線程相當於消費者,並用Runnable來表示任務,Executor的實現還提供了對生命週期的支持,以及統計信息收集,應用程序管理機制和性能監視等機制。

Executor框架主要由3大部分組成如下。

1. 任務。包括被執行任務需要實現的接口:Runnable接口或Callable接口。

2. 任務的執行。包括任務執行機制的核心接口Executor,以及繼承自Executor的ExecutorService接口。Executor框架有兩個關鍵類實現了ExecutorService接口ThreadPoolExecutorScheduledThreadPoolExecutor)。

3. 異步計算的結果。包括接口Future和實現Future接口的FutureTask類。


  • Executor是一個接口,他是Executor框架的基礎,它將任務的提交與任務的執行分離。
  • ThreadPoolExecutor是線程池的核心實現類,用來執行被提交的任務。
  • ScheduledThreadPoolExecutor是一個實現類,可以在給定的延遲後運行命令,或者定期執行命令。ScheduledThreadPoolExecutor 比 Timer 更靈活,功能更強大。
  • Future接口和它的實現FutureTask類,代表異步計算的結果。
  • Runnable和Callable接口的實現類,都可以被ThreadPoolExecutor 或 ScheduledThreadPoolExecutor 執行。


Executor框架的使用:


  1. 主線程首先要創建實現 Runnable接口或者Callable接口的任務對象。工具類Executors可以把一個Runnable對象封裝爲一個Callable對象
    1
    2
    3
    Executors.callable(Runnale task);
    Executors.callable(Runnable task, Object resule);
  2. 然後可以把Runnable對象直接交給ExecutorService執行
    1
    2
    3
    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)方法則會阻塞當前線

    程一段時間後立即返回,這時候有可能任務沒有執行完。 

最後,主線程可以執行FutureTask.get()方法來等待任務執行完成。主線程也可以執行FutureTask.cancel(boolean mayInterruptIfRunning)來取消此任務的執行。

 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(11,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
}

  

SingleThreadExecutor的corePoolSize和maxiumPoolSize都被設置1。
其他參數均與FixedThreadPool相同,其運行圖如下:

 

執行過程如下:

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>());
}

  

CachedThreadPool的corePoolSize被設置爲0,即corePool爲空;maximumPoolSize被設置爲Integer.MAX_VALUE,即maximum是無界的。這裏keepAliveTime設置爲60秒,意味着空閒的線程最多可以等待任務60秒,否則將被回收。
 
CachedThreadPool使用沒有容量的SynchronousQueue作爲主線程池的工作隊列,它是一個沒有容量的阻塞隊列。每個插入操作必須等待另一個線程的對應移除操作。這意味着,如果主線程提交任務的速度高於線程池中處理任務的速度時,CachedThreadPool會不斷創建新線程。極端情況下,CachedThreadPool會因爲創建過多線程而耗盡CPU資源。其運行圖如下:

 

執行過程如下:

1.首先執行SynchronousQueue.offer(Runnable task)。如果在當前的線程池中有空閒的線程正在執行SynchronousQueue.poll(),那麼主線程執行的offer操作與空閒線程執行的poll操作配對成功,主線程把任務交給空閒線程執行。,execute()方法執行成功,否則執行步驟2

2.當線程池爲空(初始maximumPool爲空)或沒有空閒線程時,配對失敗,將沒有線程執行SynchronousQueue.poll操作。這種情況下,線程池會創建一個新的線程執行任務。

3.在創建完新的線程以後,將會執行poll操作。當步驟2的線程執行完成後,將等待60秒,如果此時主線程提交了一個新任務,那麼這個空閒線程將執行新任務,否則被回收。因此長時間不提交任務的CachedThreadPool不會佔用系統資源。

SynchronousQueue是一個不存儲元素阻塞隊列,每次要進行offer操作時必須等待poll操作,否則不能繼續添加元素。

 

參考文章:Executor框架詳解


發佈了28 篇原創文章 · 獲贊 29 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章