1. 程式人生 > 其它 >Executor框架詳解

Executor框架詳解

Executor框架詳解

java的執行緒既是工作單元,也是執行機制。從jdk5開始,把工作單元與執行機制分離開來。工作單元包括Runnable和Callable,而執行機制由Executor框架提供。這樣一來Executor是基於生產者消費者模式的,提交任務的操作相當於生成者,執行任務的執行緒相 當於消費者。

一、Executor框架的兩級排程模型

在HotSpot VM的模型中,JAVA執行緒被一對一對映為本地作業系統執行緒。JAVA執行緒啟動時會建立一個本 地作業系統執行緒,當JAVA執行緒終止時,對應的作業系統執行緒也被銷燬回收,而作業系統會排程所有執行緒 並將它們分配給可用的CPU。
在上層,JAVA程式會將應用分解為多個任務,然後使用應用級的排程器(Executor)將這些任務對映成 固定數量的執行緒;在底層,作業系統核心將這些執行緒對映到硬體處理器上。


1、從類圖上看,Executor介面是非同步任務執行框架的基礎,該框架能夠支援多種不同型別的任務執行策略。Executor介面就提供了一個執行方法,任務是Runnbale型別,不支援Callable型別。

public interface Executor { 
    void execute(Runnable command);
}

2、ExecutorService介面實現了Executor介面,主要提供了關閉執行緒池和submit方法:

public interface ExecutorService extends Executor { 
    List<Runnable> shutdownNow();
    boolean isTerminated(); 
    <T> Future<T> submit(Callable<T> task);
}

另外該介面有兩個重要的實現類:ThreadPoolExecutor與ScheduledThreadPoolExecutor。其中ThreadPoolExecutor是執行緒池的核心實現類,用來執行被提交的任務;而 ScheduledThreadPoolExecutor是一個實現類,可以在給定的延遲後執行任務,或者定期執行命令。
在上一篇文章中,我是使用ThreadPoolExecutor來通過給定不同的引數從而建立自己所需的執行緒池,但是在後面的工作中不建議這種方式,推薦使用Exectuors工廠方法來建立執行緒池

Executor框架使用示意圖


1、主執行緒首先要建立實現Runnable或者Callable介面的任務物件工具類Executors,可以把一個Runnable物件封裝為一個Callable物件(Executors.callable(Runnable task) 或者 Executors.callable(Runnable task,Object resule))。
2、然後可以把Runnable物件直接交給ExecutorService執行(ExecutorService.execute(Runnable command));或者可以把Runnable物件或Callable物件提交給ExecutorService執行(ExecutorService.submit(Runnable task) 或 ExecutorService.submit(Callable task))。
3、如果執行ExecutorService.submit(…),ExecutorService將返回一個實現Future介面的物件(FutureTask)。由於FutureTask實現了Runnable,程式設計師也可以建立FutureTask,然後直接交給ExecutorService執行。
4、最後,主執行緒可以執行FutureTask.get()方法來等待任務執行完成。主執行緒也可以執行FutureTask.cancel(boolean mayInterruptIfRunning)來取消此任務的執行。

Executor框架的實現ThreadPoolExecutor

Executors可以建立3種類型的ThreadPoolExecutor:SingleThreadExecutorFixedThreadExecutorCachedThreadPool

1、SingleThreadExecutor:單執行緒執行緒池

ExecutorService threadPool = Executors.newSingleThreadExecutor();
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 
                                                                              0L, TimeUnit.MILLISECONDS, 
                                                                              new LinkedBlockingQueue<Runnable>()));
    }

我們從原始碼來看可以知道,單執行緒執行緒池的建立也是通過ThreadPoolExecutor,裡面的核心執行緒數和線 程數都是1,並且工作佇列使用的是無界佇列。由於是單執行緒工作,每次只能處理一個任務,所以後面所 有的任務都被阻塞在工作佇列中,只能一個個任務執行。

2、FixedThreadExecutor:固定大小執行緒池

ExecutorService threadPool = Executors.newFixedThreadPool(5);

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads, 
                                  0L, TimeUnit.MILLISECONDS, 
                                  new LinkedBlockingQueue<Runnable>());
}

這個與單執行緒類似,只是建立了固定大小的執行緒數量。

3、CachedThreadPool:無界執行緒池

ExecutorService threadPool = Executors.newCachedThreadPool();

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,  
                                  new SynchronousQueue<Runnable>());
}

無界執行緒池意味著沒有工作佇列,任務進來就執行,執行緒數量不夠就建立,與前面兩個的區別是:空閒 的執行緒會被回收掉,空閒的時間是60s。這個適用於執行很多短期非同步的小程式或者負載較輕的服務 器。