一、概述
JDK中提供了Executor框架,便於我們進行多線程操作。
關聯文章:
二、Executor 框架的兩層調度模型
說明:
從圖中可以看出,Java中執行異步任務採用兩級調度模型來完成。
在上層,應用程序通過Executor框架控制上層的調度;
在下層,調度由操作系統內核控制,下層的調度不受應用程序的控制。
二、Executor 框架的類圖
上圖是 Executor框架 提供的功能。
Executor接口: 它是Executor框架的基礎,它將任務的提交與任務的執行分離開來,無返回值;
ExecutorService接口: 提供了 submit(Runnable)、submit(Callable) 兩個方法,帶返回值;
AbstractExecutorService: 提供了兩個轉換方法,對 submit() 傳遞的參數進行包裝;
ThreadPoolExecutor: 線程池的核心類,根據參數設置的不同,可以創建出多種類型的線程池;
ScheduledExecutorService接口: 由於 ThreadPoolExecutor 執行的任務都是實時的,不具備延遲功能,因此擴展了ExecutorService接口,提供了執行任務的功能;
ScheduledThreadPoolExecutor: ScheduledExecutorService接口的具體實現類;
Executors: 一個 Executor工廠類,提供了 FixedThreadPool
、SingleThreadExecutor
、CachedThreadPool
、ScheduledThreadPoolExecutor
等類型的線程池;
三、Executor 框架包含的成員
Executor框架主要由3大部分組成。
-
任務。 包括被執行任務需要實現的接口:Runnable接口 或 Callable接口。
-
任務的執行。 包括任務執行機制的核心接口
Executor
,以及繼承自Executor的ExecutorService
接口。Executor框架提供了兩個實現了ExecutorService接口的實現類:
ThreadPoolExecutor
、ScheduledThreadPoolExecutor
。 -
異步計算的結果。 包括接口
Future
和實現Future接口的FutureTask
類。
四、小結
-
提交任務到線程池的方式有兩種:
execute()
、submit()
execute()
:不帶返回值;submit()
:帶返回值Future
;
-
提交到線程池的 task 有兩種類型:
Runnable
、Callable
Runnable
:不帶返回值的 task,execute(Runnable)
和submit(Runnable)
;Callable
:帶返回值的 task,submit(Callable)
;
-
線程池只能執行 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; }
-
RunnableFuture
、FutureTask
的介紹。RunnableFuture
接口:繼承了Runnable 接口
和Future 接口
。FutureTask
類:實現了RunnableFuture 接口
。
FutureTask 內部只保存Callable
類型的變量,但是FutureTask構造接收Runnable
、Callable
兩種類型的參數。因此,當接收到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接口。 }
-
由第1點、第2點和第3點可以得出以下結論:
- 如果是由
execute(Runnable)
提交的 task(Runnable類型),則線程池可以直接執行。 - 如果是由
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點 }
- 如果是由
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點 }
- 如果是由
-
通過返回的
Future
進行取值。由submit()
提交任務後會返回一個Future
,所以我們可以通過Future.get()
取值(線程阻塞)。