Executor框架(一) — 淺析 Executor 框架設計

一、概述

JDK中提供了Executor框架,便於我們進行多線程操作。

關聯文章:

  1. Executor框架(二) — 線程池原理淺析
  2. Executor框架(三) — 幾種常見的線程池
  3. Executor框架(四) — ThreadPoolExecutor源碼解析

二、Executor 框架的兩層調度模型

在這裏插入圖片描述
說明:
從圖中可以看出,Java中執行異步任務採用兩級調度模型來完成。
在上層,應用程序通過Executor框架控制上層的調度;
在下層,調度由操作系統內核控制,下層的調度不受應用程序的控制。


二、Executor 框架的類圖

這裏寫圖片描述
上圖是 Executor框架 提供的功能。

Executor接口: 它是Executor框架的基礎,它將任務的提交與任務的執行分離開來,無返回值;
ExecutorService接口: 提供了 submit(Runnable)、submit(Callable) 兩個方法,帶返回值;
AbstractExecutorService: 提供了兩個轉換方法,對 submit() 傳遞的參數進行包裝;
ThreadPoolExecutor: 線程池的核心類,根據參數設置的不同,可以創建出多種類型的線程池;
ScheduledExecutorService接口: 由於 ThreadPoolExecutor 執行的任務都是實時的,不具備延遲功能,因此擴展了ExecutorService接口,提供了執行任務的功能;
ScheduledThreadPoolExecutor: ScheduledExecutorService接口的具體實現類;
Executors: 一個 Executor工廠類,提供了 FixedThreadPoolSingleThreadExecutorCachedThreadPoolScheduledThreadPoolExecutor 等類型的線程池;


三、Executor 框架包含的成員

在這裏插入圖片描述
在這裏插入圖片描述
Executor框架主要由3大部分組成。

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

  2. 任務的執行。 包括任務執行機制的核心接口 Executor,以及繼承自Executor的 ExecutorService 接口。

    Executor框架提供了兩個實現了ExecutorService接口的實現類:ThreadPoolExecutorScheduledThreadPoolExecutor

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

四、小結

  1. 提交任務到線程池的方式有兩種:execute()submit()

    1. execute():不帶返回值;
    2. submit():帶返回值 Future
  2. 提交到線程池的 task 有兩種類型: RunnableCallable

    1. Runnable:不帶返回值的 task,execute(Runnable)submit(Runnable)
    2. Callable:帶返回值的 task, submit(Callable)
  3. 線程池只能執行 Runnable 類型的 task,這個可以由 Executor.execute(Runnable) 看出;

    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        //RunnableFuture接口實現了Runnable和Future接口
        RunnableFuture<Void> ftask = newTaskFor(task, null); 
        execute(ftask); //submit(Runnable)方法最終還是調用execute(Runnable)
        return ftask;
    }
    
    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);  //submit(Callable)方法最終還是調用execute(Runnable)
        return ftask;
    }
    
  4. RunnableFutureFutureTask 的介紹。

    1. RunnableFuture 接口:繼承了 Runnable 接口Future 接口
    2. FutureTask 類:實現了 RunnableFuture 接口
      FutureTask 內部只保存 Callable 類型的變量,但是FutureTask構造接收 RunnableCallable 兩種類型的參數。因此,當接收到 Runnable 類型的參數時,需要調用 Executors.callable(runnable, result) 方法進行轉換 。
    // RunnableFuture.class
    // 具備了提交給線程池執行的能力(Runnable),也具備取出返回值的能力(Future)
    public interface RunnableFuture<V> extends Runnable, Future<V> {
    	    void run();
    }
    // FutureTask.class
    public FutureTask(Callable<V> callable) {
    	if (callable == null)
    		throw new NullPointerException();
    	this.callable = callable;
    	this.state = NEW;   // ensure visibility of callable
    }
    // FutureTask.class
    public FutureTask(Runnable runnable, V result) {
    	//由於FutureTask只接收Callable,所以這裏需要做一下轉換。
        this.callable = Executors.callable(runnable, result); 
        this.state = NEW;  // ensure visibility of callable
    }
    // Executor.class
    public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result); //RunnableAdapter實現了Callable接口。
    }
    
  5. 由第1點、第2點和第3點可以得出以下結論:

    1. 如果是由 execute(Runnable) 提交的 task(Runnable類型),則線程池可以直接執行。
    2. 如果是由 submit(Runnable) 提交的 task(Runnable類型),由於需要返回一個 Future 對象接收數據,因此線程池不能直接執行 task,需要對 Runnable類型的 task 進行包裝 。可以參考第4點。
    // AbstractExecutorService.class
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null); //RunnableFuture接口實現了Runnable和Future接口
        execute(ftask); //submit(Runnable)方法最終還是調用execute(Runnable)
        return ftask; //返回的RunnableFuture實現了Future接口
    }
    // AbstractExecutorService.class
    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value); //可以參考第4點
    }
    
    1. 如果是由 submit(Callable) 提交的 task(Callable類型),由於線程池只能執行 Runnable 類型的 task,且需要返回一個 Future 對象接收數據,因此線程池不能直接執行 task,需要對 Callable 類型的 task 進行包裝 。//可以參考第4點。
    // AbstractExecutorService.class
    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task); //RunnableFuture接口實現了Runnable和Future接口
        execute(ftask);  //submit(Callable)方法最終還是調用execute(Runnable)
        return ftask; //返回的RunnableFuture實現了Future接口
    }
    // AbstractExecutorService.class
    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable); //可以參考第4點
    }
    
  6. 通過返回的 Future 進行取值。由 submit() 提交任務後會返回一個 Future ,所以我們可以通過 Future.get() 取值(線程阻塞)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章