Table of Contents
介紹
AbstractExecutorService 是 JDK 線程池 ThreadPoolExecutor,ForkJoinPool 等的父類,提供了許多非常有用的功能。
AbstractExecutorService 的繼承關係如下:
Executor
Executor 是執行 Runnable 任務的接口,用於解耦任務創建和任務執行,包括線程的使用和執行的排期等。在需要創建多個線程執行任務時,Executor 還可以代替 new Thread(new(RunnableTask())).start(),而使用
Executor executor = anExecutor;
executor.execute(new RunnableTask1());
executor.execute(new RunnableTask2());
然而 Executor 並不要求任務按照異步的方式執行,也可以只是使用當前線程執行任務,比如:
class DirectExecutor implements Executor {
public void execute(Runnable r) {
r.run();
}
}
但是 Executor 的實現類一般來說都是使用異步的方式執行提交的任務,如下:
class ThreadPerTaskExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
}
}
Executor 接口方法定義如下:
/**
*
* 在接下來的某個時刻執行任務。根據實現類的不同,任務可能在調用方的線程中執行,也可能創建一個新的線程執行,或者在一個線程池中執行。
*
* @param command the runnable task 可執行的任務(實現 Runnable 接口的對象)
* @throws RejectedExecutionException if this task cannot be
* accepted for execution 如果 Executor 不接受任務則拋出該異常
* @throws NullPointerException if command is null 如果任務爲爲空則拋出該異常
*/
void execute(Runnable command);
ExecutorService
ExecutorService 提供一些方法用於管理服務終止以及提供一些返回計算結果(Future) 的執行任務的方法。Future(計算結果)可以用於跟蹤異步任務的執行結果,查看是否完成,取消未完成的任務等。
ExecutorService 可以被關閉。被關閉的 ExecutorService 無法再執行異步任務。ExecutorService 提供了兩個關閉 ExecutorService 的方法:
- shutdown:該方法會繼續執行在調用該方法前提交的任務,但是會拒絕之後提交的任務。在之前提交的任務全部結束後關閉 ExecutorService。
- shutdownNow: 該方法會暫停正在進行中和等待中的任務。
ExecutorService 如果未在使用,那麼應該關閉回收計算機資源。
ExecutorService 提供 submit 方法,用於擴展 execute。submit 能夠返回一個 Future。invokeAny 和 invokeAll 能夠處理一批的任務,並等待一個或者所有的異步任務完成。
ExecutorService 使用例子:
class NetworkService implements Runnable {
private final ServerSocket serverSocket;
private final ExecutorService pool;
public NetworkService(int port, int poolSize)
throws IOException {
serverSocket = new ServerSocket(port);
pool = Executors.newFixedThreadPool(poolSize);
}
public void run() { // run the service
try {
for (;;) {
pool.execute(new Handler(serverSocket.accept()));
}
} catch (IOException ex) {
pool.shutdown();
}
}
}
class Handler implements Runnable {
private final Socket socket;
Handler(Socket socket) { this.socket = socket; }
public void run() {
// read and service request on socket
}
}
關停 ExecutorService 的例子:
void shutdownAndAwaitTermination(ExecutorService pool) {
// 拒接接受新任務
pool.shutdown();
try {
// 如果服務在 60 秒還未關停則做更多的處理
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
//立即關停所有任務
pool.shutdownNow();
// 如果服務在 60 秒還未關停則輸出提示
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
System.err.println("Pool did not terminate");
}
} catch (InterruptedException ie) {
// 如果遇到線程中斷異常那麼立即關停服務並將線程置爲中斷狀態
pool.shutdownNow();
Thread.currentThread().interrupt();
}
}
ExecutorService 提供的內存一致性保證:提交任務操作一定在線程處理任務之前完成,並且線程處理任務一定在 Future.get()獲取到異步任務結果前完成。
方法定義
/**
* 開始關停 ExecutorService。
* 關停前提交的任務會繼續執行,但是不會接受新任務。如果是已經關停的 ExecutorService 調用該方法不會發生任何變換。
*
* @throws SecurityException 如果存在安全管理器並且關閉線程沒有權限修改線程狀態,那麼會拋出該異常。沒有權限修改的原因是沒有獲得 RuntimePermission 或者是安全管理器檢查訪問權限時校驗未通過。
*/
void shutdown();
/**
* 立即關停 ExecutorService
* 嘗試關停所有進行中的任務,同時拒絕任何新任務,停止處理等待中的任務,並且返回等待任務的列表。
* 正在進行中的任務沒法保證一定成功停止,比如通過線程中斷方法(Thread.interrupt)中斷進行中的任務,而任務沒有響應線程中斷,那麼任務無法終止。
*
* @return 正在進行中的任務列表
* @throws SecurityException 如果存在安全管理器並且關閉線程沒有權限修改線程狀態,那麼會拋出該異常。沒有權限修改的原因是沒有獲得 RuntimePermission 或者是安全管理器檢查訪問權限時校驗未通過。
*/
List<Runnable> shutdownNow();
/**
* 返回關閉狀態:如果關閉則返回 true,否則返回 false
*
* @return 關閉狀態:如果關閉則返回 true,否則返回 false
*/
boolean isShutdown();
/**
* 如果關閉後所有任務都完成那麼返回 true
*
* @return 如果關閉後所有任務都完成那麼返回 true
*/
boolean isTerminated();
/**
* 當關閉 ExecutorService 之後用於阻塞等待給定的時間,讓正在進行中的任務完成,直到任意條件滿足:
* 1. 所有任務都已經完成
* 2. 等待超時
* 3. 線程被中斷
*
* @param 等待的時間
* @param unit the time unit of the timeout argument 時間單位
* @return 如果 ExecutorService 在時間內終止了那麼返回 true,否則返回 false
* @throws InterruptedException 當前線程被中斷拋出中斷異常
*/
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
/**
* 提交一個可執行的並且由返回值的任務,並返回一個任務執行結果(Future)。
* 如果希望立即阻塞直到獲取異步任務結果可以使用 result = exec.submit(aCallable).get()。Future.get()或阻塞直到獲取到異步任務結果。
* ExecutorService 提供了很多方法用於將閉包對象轉成被執行的方法。
*
* @param 可被 ExecutorService 執行的任務
* @param <T> 任務的執行結束後返回的結果的對象類型
* @return 可以跟蹤異步任務執行結果的對象(Future)
* @throws RejectedExecutionException 如果 ExecutorService 拒絕任務則拋出
* @throws NullPointerException if the task is null
*/
<T> Future<T> submit(Callable<T> task);
/**
* 提交一個可執行的任務,並指定任務返回的對象類型,該方法返回一個可以跟蹤任務執行結果(Future)
* @param task 可執行的任務
* @param Result 該任務執行後返回的類型
* @param <T>任務的執行結束後返回的結果的對象類型
* @return 任務執行結果(Future)
* @throws RejectedExecutionException ExecutorService 拒絕任務後拋出該異常
* @throws NullPointerException 如果任務(task)是 null 拋出該異常
*/
<T> Future<T> submit(Runnable task, T result);
/**
* 提交一個可執行的任務,並返回一個可以跟蹤任務執行結果的對象(Future)
*
* @param task 可執行的任務
* @return 任務執行結果(Future)
* @throws RejectedExecutionException 如果 ExecutorService 拒絕該任務則拋出該異常
* @throws NullPointerException 如果提交了一個空任務則拋出該異常
*/
Future<?> submit(Runnable task);
/**
* 執行批量任務並返回跟蹤這批任務執行結果的對象集合(List<Future>)
* 該方法會阻塞等待任務結束後纔將結果返回,所以調用方拿到結果時,任務已經結束(或者任務執行過程中斷導致的異常結束)。
* 如果在 invokeAll 執行期間改變任務集合可能會引發未定義方法說明上(throws 定義的異常列表)的異常。
* 比如 invokeAll 使用 for 循環遍歷集合時集合被刪除元素拋出 ConcurrentModificationException 異常。
*
* @param tasks 任務集合
* @param <T> 任務類型
* @return 任務執行結果(Future)集合
* @throws InterruptedException 如果在等待結果過程中線程被中斷則拋出該異常
* @throws NullPointerException 如果任務結合是空對象則拋出該異常
* @throws RejectedExecutionException 如果任務提交被拒絕則拋出該異常
*/
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
/**
* 在上一個 invokeAll 基礎上增加了等待時間。
*
* @param tasks 任務集合
* @param timeout 等待時間
* @param unit 時間單位
* @param <T> 任務完成後返回類型
* @throws InterruptedException 如果在等待過程中線程被中斷則拋出該異常
* @throws NullPointerException 如果提交的任務集合是空對象則拋出該異常
* @throws RejectedExecutionException 如果本次提交被拒絕則拋出該異常
*/
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
/**
* 執行給定任務列表中的任一任務。
* 如果有任一任務執行完成則將該任務執行結果返回,並且取消其他任務。
*
* @param tasks 任務集合
* @param <T> 任務返回的類型
* @return 任務執行結果(Future)
* @throws InterruptedException 如果等待過程中線程被中斷則拋出中斷異常
* @throws NullPointerException 如果提交的任務
* @throws IllegalArgumentException 如果任務是空列表則拋出該異常
* @throws ExecutionException 如果沒有任何任務執行成功則拋出該異常
* @throws RejectedExecutionException 如果任務被拒絕則拋出該異常
*/
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
/**
* 在上一個 invokeAny 基礎上增加等待時間
*
* @param tasks the collection of tasks
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
* @param <T> the type of the values returned from the tasks
* @return the result returned by one of the tasks
* @throws InterruptedException if interrupted while waiting
* @throws NullPointerException if tasks, or unit, or any element
* task subject to execution is {@code null}
* @throws TimeoutException if the given timeout elapses before
* any task successfully completes
* @throws ExecutionException if no task successfully completes
* @throws RejectedExecutionException if tasks cannot be scheduled
* for execution
*/
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
CompletionService
CompletionService 用於給創建異步任務和消費異步任務結果解耦。CompletionService 依賴 Executor 執行異步任務,而自己管理完成隊列,維護異步任務執行結果。
/**
* 提交任務
*
* @param task 任務
* @return 任務執行結果(Future)
* @throws RejectedExecutionException 如果拒絕任務則拋出該異常
* @throws NullPointerException 如果任務是空對象則拋出該異常
*/
Future<V> submit(Callable<V> task);
/**
* 同上
*
* @param task the task to submit
* @param result the result to return upon successful completion
* @return a Future representing pending completion of the task,
* and whose {@code get()} method will return the given
* result value upon completion
* @throws RejectedExecutionException if the task cannot be
* scheduled for execution
* @throws NullPointerException if the task is null
*/
Future<V> submit(Runnable task, V result);
/**
* 獲取計算結果並在隊列中刪除該計算結果。如果沒有任務任務完成,那麼該方法將會阻塞線程等待任務計算結果
*
* @return 計算結果
* @throws InterruptedException 在等待過程中線程被中斷則拋出該異常
*/
Future<V> take() throws InterruptedException;
/**
* 同 take(),不一樣的地方時該方法不會阻塞,即如果沒有任務完成則返回 null。
*
* @return 任務計算結果
*/
Future<V> poll();
/**
* 通 take(),只是增加了等待超時時間的設置。
*
* @param timeout how long to wait before giving up, in units of
* {@code unit}
* @param unit a {@code TimeUnit} determining how to interpret the
* {@code timeout} parameter
* @return the Future representing the next completed task or
* {@code null} if the specified waiting time elapses
* before one is present
* @throws InterruptedException if interrupted while waiting
*/
Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;
ExecutorCompletionService
ExecutorCompletionService 是 CompletionService 實現類。ExecutorCompletionService 利用 AbstractExecutorService 構建 RunnableFuture(一個即表示任務又表示計算結果的對象),通過 Executor 提交任務,並在任務直接結束後將計算結果存到阻塞隊列中。
public class ExecutorCompletionService<V> implements CompletionService<V> {
//任務執行者,用於提交任務
private final Executor executor;
// 任務執行服務,用戶構建 RunnableFuture
private final AbstractExecutorService aes;
// 阻塞隊列。用於存儲任務的計算結果
private final BlockingQueue<Future<V>> completionQueue;
/**
* 自定義一個計算結果,主要實現完成異步任務時,將計算結果加到阻塞隊列中。
*/
private class QueueingFuture extends FutureTask<Void> {
QueueingFuture(RunnableFuture<V> task) {
super(task, null);
this.task = task;
}
// FutureTask 留了一個鉤子,用於完成任務後執行相關功能。本身 FutureTask 沒有任務實現,這裏將計算結果加到阻塞隊列中。
protected void done() { completionQueue.add(task); }
private final Future<V> task;
}
// 自定義構建 RunnableFuture
private RunnableFuture<V> newTaskFor(Callable<V> task) {
if (aes == null)
return new FutureTask<V>(task);
else
return aes.newTaskFor(task);
}
// 自定義構建 RunnableFuture
private RunnableFuture<V> newTaskFor(Runnable task, V result) {
if (aes == null)
return new FutureTask<V>(task, result);
else
return aes.newTaskFor(task, result);
}
/**
* 構造方法
*
* @param executor the executor to use
* @throws NullPointerException if executor is {@code null}
*/
public ExecutorCompletionService(Executor executor) {
if (executor == null)
throw new NullPointerException();
this.executor = executor;
this.aes = (executor instanceof AbstractExecutorService) ?
(AbstractExecutorService) executor : null;
this.completionQueue = new LinkedBlockingQueue<Future<V>>();
}
/**
* 構造方法
*
* @param executor the executor to use
* @param completionQueue the queue to use as the completion queue
* normally one dedicated for use by this service. This
* queue is treated as unbounded -- failed attempted
* {@code Queue.add} operations for completed tasks cause
* them not to be retrievable.
* @throws NullPointerException if executor or completionQueue are {@code null}
*/
public ExecutorCompletionService(Executor executor,
BlockingQueue<Future<V>> completionQueue) {
if (executor == null || completionQueue == null)
throw new NullPointerException();
this.executor = executor;
this.aes = (executor instanceof AbstractExecutorService) ?
(AbstractExecutorService) executor : null;
this.completionQueue = completionQueue;
}
public Future<V> submit(Callable<V> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task);
executor.execute(new QueueingFuture(f));
return f;
}
public Future<V> submit(Runnable task, V result) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task, result);
executor.execute(new QueueingFuture(f));
return f;
}
public Future<V> take() throws InterruptedException {
return completionQueue.take();
}
public Future<V> poll() {
return completionQueue.poll();
}
public Future<V> poll(long timeout, TimeUnit unit)
throws InterruptedException {
return completionQueue.poll(timeout, unit);
}
AbstractExecutorService
AbstractExecutorService 實現了 ExecutorService 定義的執行任務的方法,比如 submit,invokeAll,invokeAny 等。AbstractExecutorService 提供了一個 newTaskFor 方法用於構建 RunnableFuture 對象。執行任務方法返回的跟蹤任務執行結果的對象都是通過 newTaskFor 來構建的。如果有需要可以通過自定義 newTaskFor 來構建所需的 RunnableFuture。
/**
* 構建用於跟蹤任務執行結果的對象。
* 如果有需要可以重寫該方法自定義跟蹤任務結果對象
*
* @param runnable 可執行任務
* @param value 任務執行結果類型
* @param <T> 任務執行結果的類型
* @return 任務執行結果
* @since 1.6
*/
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
/**
* 同上
*
* @param callable the callable task being wrapped
* @param <T> the type of the callable's result
* @return a {@code RunnableFuture} which, when run, will call the
* underlying callable and which, as a {@code Future}, will yield
* the callable's result as its result and provide for
* cancellation of the underlying task
* @since 1.6
*/
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
/**
* 提交任務
*/
public Future<?> submit(Runnable task) {
// 校驗任務,如果是空對象則拋出空指針異常
if (task == null) throw new NullPointerException();
// 構建一個 RunnableFuture 對象。RunnableFuture 本身表示一個任務(extends Runnable),同時也表示一個任務執行結果(extends Future)。
RunnableFuture<Void> ftask = newTaskFor(task, null);
// 執行任務
execute(ftask);
// 將 RunnableFuture 對象返回
return ftask;
}
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
/**
* 執行批量任務中的任一任務
*/
private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,
boolean timed, long nanos)
throws InterruptedException, ExecutionException, TimeoutException {
// 如果任務是空對象拋出空指針異常
if (tasks == null)
throw new NullPointerException();
int ntasks = tasks.size();
// 如果任務集合是空集合拋出參數不合法異常
if (ntasks == 0)
throw new IllegalArgumentException();
// 構建計算結果集合
ArrayList<Future<T>> futures = new ArrayList<Future<T>>(ntasks);
// 任務提交和消費的生產者消費者工具
ExecutorCompletionService<T> ecs =
new ExecutorCompletionService<T>(this);
// 每次提交前都先檢查一下是否有完成的任務,如果完成則直接返回完成任務的計算結果,否則繼續提交剩餘任務
try {
//異常信息。用於最後一次獲取的異常信息。如果任務沒有正常結束,將該異常信息拋出。
ExecutionException ee = null;
// 等待時間
final long deadline = timed ? System.nanoTime() + nanos : 0L;
Iterator<? extends Callable<T>> it = tasks.iterator();
// 先提交一個任務,以後再循環時先檢查是否有任務已經完成。
// 利用生產者消費者工具提交任務。任務如果結束後,會將計算結果存入到工具的阻塞隊列中。
futures.add(ecs.submit(it.next()));
--ntasks;
int active = 1;
for (;;) {
// 檢查是否有任務已經完成,如果有那麼返回任務結果,否則繼續提交任務
Future<T> f = ecs.poll();
if (f == null) {
// 還有任務未提交完,繼續提交任務
if (ntasks > 0) {
--ntasks;
futures.add(ecs.submit(it.next()));
++active;
}
// 所有任務已執行結束,那麼跳出循環。如果到這應該有執行異常的結果,後續會拋出執行異常
else if (active == 0)
break;
// 如果設置超時時間,那麼利用阻塞隊列的等待阻塞獲取異常結果。如果超時未獲取到計算結果,那麼拋出超時異常。
else if (timed) {
f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
if (f == null)
throw new TimeoutException();
// 正常獲取到結果,那麼繼續更新等待剩餘時間。以防止 f.get()異常後還需要走這段代碼,需要繼續阻塞等待。
nanos = deadline - System.nanoTime();
}
// 如果沒有設置超時時間,那麼無限等待獲取計算結果
else
f = ecs.take();
}
// 如果獲取到計算結果,那麼將活躍任務(active)數減一,並且返回具體的任務結果。
if (f != null) {
--active;
// 如果獲取計算結果有異常,那麼記錄異常信息,繼續循環獲取其他任務的計算結果
try {
return f.get();
} catch (ExecutionException eex) {
ee = eex;
} catch (RuntimeException rex) {
ee = new ExecutionException(rex);
}
}
}
// 到此任務已經全部結束,但是沒有任何一個任務正常結束。如果記錄的異常不爲空則拋出記錄的異常信息,否則創建一個執行任務異常並拋出。
if (ee == null)
ee = new ExecutionException();
throw ee;
} finally {
// 取消所有任務。到這或者是有一個任務已經結束(結束任務依然可以取消,只是沒有效果)需要將其他任務取消,或者所有任務都非正常結束(取消任務沒有效果)。
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
try {
return doInvokeAny(tasks, false, 0);
} catch (TimeoutException cannotHappen) {
assert false;
return null;
}
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return doInvokeAny(tasks, true, unit.toNanos(timeout));
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
if (tasks == null)
throw new NullPointerException();
ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
// 任務是否全部結束標誌
boolean done = false;
try {
// 構建 RunnableFuture
// 將計算結果存結果集中
// 執行任務
for (Callable<T> t : tasks) {
RunnableFuture<T> f = newTaskFor(t);
futures.add(f);
execute(f);
}
// 循環遍歷計算結果,如果有未完成,那麼阻塞等待任務計算完成
for (int i = 0, size = futures.size(); i < size; i++) {
Future<T> f = futures.get(i);
if (!f.isDone()) {
try {
f.get();
} catch (CancellationException ignore) {
} catch (ExecutionException ignore) {
}
}
}
done = true;
return futures;
} finally {
// done == false 表示在執行任務時(execute(f))或者之前已經出現異常情況,比如當前線程被中斷導致無法全部提交,那麼將任務全部取消釋放資源。
if (!done)
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException {
if (tasks == null)
throw new NullPointerException();
long nanos = unit.toNanos(timeout);
ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
boolean done = false;
try {
// 構建計算結果並加到結果集中
for (Callable<T> t : tasks)
futures.add(newTaskFor(t));
// 超時時間節點
final long deadline = System.nanoTime() + nanos;
final int size = futures.size();
// 循環遍歷結果集並執行任務。每次執行後立即檢查超時時間。如果超時則直接返回結果集。既然已經超時,後續任務已經沒必要開始。
for (int i = 0; i < size; i++) {
execute((Runnable)futures.get(i));
nanos = deadline - System.nanoTime();
if (nanos <= 0L)
return futures;
}
// 循環遍歷結果集,如果沒有完成則阻塞等待完成。
for (int i = 0; i < size; i++) {
Future<T> f = futures.get(i);
if (!f.isDone()) {
if (nanos <= 0L)
return futures;
try {
f.get(nanos, TimeUnit.NANOSECONDS);
} catch (CancellationException ignore) {
} catch (ExecutionException ignore) {
} catch (TimeoutException toe) {
return futures;
}
nanos = deadline - System.nanoTime();
}
}
done = true;
return futures;
} finally {
if (!done)
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}