天天看點

Java 并發程式設計 11 - 異步執行架構 Executor

作者:散文随風想

1 Executor 架構的簡介

1.5 後引入的 Executor 架構的最大優點是把任務的送出和執行解耦。要執行任務的人隻需把 Task 描述清楚,然後送出即可。這個 Task 是怎麼被執行的,被誰執行的,什麼時候執行的,送出的人就不用關心了。具體點講,送出一個 Callable 對象給 ExecutorService(如最常用的線程池 ThreadPoolExecutor),将得到一個 Future 對象,調用 Future 對象的 get 方法等待執行結果就好了。

2 一個最簡單的例子

看上去這個執行過程是這個樣子。調用這段代碼的是老大的老大了,他所需要幹的所有事情就是找到一個合适的老大(如下面例子中 laodaA 就榮幸地被選中了),送出任務就好了。

// 一個有7個作業線程的線程池,老大的老大找到一個管7個人的小團隊的老大
		ExecutorService laodaA = Executors.newFixedThreadPool(7);
		// 送出作業給老大,作業内容封裝在Callable中,約定好了輸出的類型是String。
		String outputs = laoda.submit(new Callable<String>() {
			public String call() throws Exception {
				return "I am a task, which submited by the so called laoda, and run by those anonymous workers";
			}
			// 送出後就等着結果吧,到底是手下7個作業中誰領到任務了,老大是不關心的。
		}).get();

		System.out.println(outputs);           

使用上非常簡單,其實隻有兩行語句來完成所有功能:建立一個線程池,送出任務并等待擷取執行結果。

3 Executor 架構的結構

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

任務:包括被執行任務需要實作的接口:Runnable 接口或 Callable 接口。 任務的執行:包括任務執行機制的核心接口 Executor,以及繼承自 Executor 的 ExecutorService 接口。Executor 架構有兩個關鍵類實作了 ExecutorService 接口(ThreadPoolExecutor 和 ScheduledThreadPoolExecutor)。 異步計算的結果:包括接口 Future 和實作 Future 接口的 FutureTask 類。

Executor 架構包含的主要的類與接口如下圖所示。

Java 并發程式設計 11 - 異步執行架構 Executor

4 Executor 架構的接口簡介

下面是這些類和接口的簡介。

Executor 是一個接口,它是 Executor 架構的基礎,它将任務的送出與任務的執行分離開來。 ThreadPoolExecutor 是線程池的核心實作類,用來執行被送出的任務。 ScheduledThreadPoolExecutor 是一個實作類,可以在給定的延遲後運作指令,或者定期執行指令。ScheduledThreadPoolExecutor 比 Timer 更靈活,功能更強大。 Future 接口和實作 Future 接口的 FutureTask 類,代表異步計算的結果。 Runnable 接口和 Callable 接口的實作類,都可以被 ThreadPoolExecutor 或 Scheduled-ThreadPoolExecutor 執行。它們之間的差別是 Runnable 不會傳回結果,而 Callable 可以傳回結果。

除了可以自己建立實作 Callable 接口的對象外,還可以使用工廠類 Executors 來把一個 Runnable 包裝成一個 Callable。

下面是 Executors 提供的,把一個 Runnable 包裝成一個 Callable 的 API。

public static Callable<Object> callable(Runnable task) // 假設傳回對象Callable1           

下面是 Executors 提供的,把一個 Runnable 和一個待傳回的結果包裝成一個 Callable 的 API。

public static <T> Callable<T> callable(Runnable task, T result) // 假設傳回對象Call           
前面講過,當我們把一個 Callable 對象(比如上面的 Callable1 或 Callable2)送出給 ThreadPoolExecutor 或 ScheduledThreadPoolExecutor 執行時,submit(…)會向我們傳回一個 FutureTask 對象。我們可以執行 FutureTask.get () 方法來等待任務執行完成。當任務成功完成後 FutureTask.get () 将傳回該任務的結果。例如,如果送出的是對象 Callable1,FutureTask.get () 方法将傳回 null;如果送出的是對象 Callable2,FutureTask.get () 方法将傳回 result 對象。

5 Executor 架構的使用

Executor 架構的使用示意圖如下圖所示。

Java 并發程式設計 11 - 異步執行架構 Executor

主線程首先要建立實作 Runnable 或者 Callable 接口的任務對象。工具類 Executors 可以把一個 Runnable 對象封裝為一個 Callable 對象(Executors.callable(Runnable task)或 Executors.callable(Runnable task,Object resule))。

然後可以把 Runnable 對象直接交給 ExecutorService 執行(ExecutorService.execute(Runnablecommand));或者也可以把 Runnable 對象或 Callable 對象送出給 ExecutorService 執行(Executor-Service.submit(Runnable task)或 ExecutorService.submit(Callable<T>task))。

如果執行 ExecutorService.submit(…),ExecutorService 将傳回一個實作 Future 接口的對象(到目前為止的 JDK 中,傳回的是 FutureTask 對象)。由于 FutureTask 實作了 Runnable,程式員也可以建立 FutureTask,然後直接交給 ExecutorService 執行。

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

繼續閱讀