Executor
public interface Executor {
void execute(Runnable command);
}
Executor抽象提供了一種將任務提交與每個任務的運行機制(包括線程使用、調度)分離的方法,即Runnable
代表任務,execute
處理調度的邏輯;
static class ThreadPerTaskExecutor implements Executor {
public void execute(Runnable r) {
System.out.println("ThreadPerTaskExecutor - execute");
new Thread(r).start();
}
}
static class DirectExecutor implements Executor {
public void execute(Runnable r) {
System.out.println("DirectExecutor - execute");
r.run();
}
}
class SerialExecutor implements Executor {
final Queue<Runnable> tasks = new ArrayDeque<Runnable>();
final Executor executor;
Runnable active;
SerialExecutor(Executor executor) {
this.executor = executor;
}
public synchronized void execute(final Runnable r) {
System.out.println("task offer ... ");
tasks.offer(new Runnable() {
public void run() {
System.out.println("SerialExecutor - execute ... ");
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (active == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((active = tasks.poll()) != null) {
System.out.println("schedule next task ");
executor.execute(active);
}
}
}
public static void main(String[] args) {
// SerialExecutor executor = new SerialExecutor(new ThreadPerTaskExecutor());
SerialExecutor executor = new SerialExecutor(new DirectExecutor());
executor.execute(() -> { System.out.println(Thread.currentThread()); });
executor.execute(() -> { System.out.println(Thread.currentThread()); });
}
#輸出
#task offer ...
#schedule next task
#DirectExecutor - execute
#SerialExecutor - execute ...
#Thread[main,5,main]
#task offer ...
#schedule next task
#DirectExecutor - execute
#SerialExecutor - execute ...
#Thread[main,5,main]
官方文檔上提供的示例,演示了Executor
抽象的簡單使用方法,示例想體現的除了異步同步任務外,我覺得更加重要的是任務執行和任務調度分離的思想;
ExecutorService
public interface ExecutorService extends Executor {
// 啟動有序關閉,在此過程中執行以前提交的任務,但不接受任何新任務。
void shutdown();
// 嘗試停止所有正在執行的任務,停止等待任務的處理,并返回等待執行的任務列表。
List<Runnable> shutdownNow();
// executor是否被shutdown
boolean isShutdown();
// 如果所有任務都完成關閉則返回true
boolean isTerminated();
// 阻塞直到所有任務在關閉請求后完成執行,或超時發生后,或當前線程中斷后;
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
// 提交任務,返回
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
// 執行一個集合的任務,返回一個列表其中包含所有任務完成時的狀態和結果(可以是異常結果)。
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
// 執行給定的任務,返回已成功完成的任務的結果(即不拋出異常),如果有的話。在正常或異常返回時,未完成的任務將被取消。
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
ExecutorService提供了對每個Executor
跟蹤、管理一個或者多個異步任務的進度,也可以關閉服務來終止service接收新任務和回收資源;
void shutdownAndAwaitTermination(ExecutorService pool) {
pool.shutdown(); // 禁止提交新任務
try {
//等待現有任務終止
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow(); // 取消當前正在執行的任務
// 等待一段時間,等待任務響應被取消
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
System.err.println("Pool did not terminate");
}
} catch (InterruptedException ie) {
// 如果當前線程處于中斷狀態,重試shutdown
pool.shutdownNow();
// 保存中斷狀態
Thread.currentThread().interrupt();
}
}
文檔中演示了一個終止服務的示例,分兩個階段關閉一個 ExecutorService
,首先需要調用shutdown
來拒絕傳入的任務,然后調用shutdownNow
(如果需要的話)來取消任何延遲的任務;
CompletionService
public interface CompletionService<V> {
Future<V> submit(Callable<V> task);
Future<V> submit(Runnable task, V result);
Future<V> take() throws InterruptedException;
Future<V> poll();
Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;
}
CompletionService旨在提供一個異步任務產生執行和已完成任務結果的解耦服務,即會有一個隊列儲存已完成的任務結果的合集,任務的提交和執行不會阻塞獲取結果操作;
ExecutorCompletionService
public class ExecutorCompletionService<V> implements CompletionService<V> {
private final Executor executor;
private final AbstractExecutorService aes;
private final BlockingQueue<Future<V>> completionQueue;
...
}
ExecutorCompletionService是CompletionService的實現類,內部有個阻塞隊列儲存的是完成任務的結果;
// 注意這個queue的默認長度為Integer.MAX_VALUE
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>>();
}
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;
}
構造器默認使用LinkedBlockingQueue
,那么很容易就聯想到該阻塞隊列的特性:
- 是有界的雙端隊列
- 提供阻塞和非阻塞方法獲取已完成的任務
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);
}
- 當完成任務的隊列滿了,新完成的任務結果會被拋棄
private class QueueingFuture extends FutureTask<Void> {
QueueingFuture(RunnableFuture<V> task) {
super(task, null);
this.task = task;
}
// FutureTask提供的鉤子
protected void done() { completionQueue.add(task); }
private final Future<V> task;
}
注意這里的done
在FutureTask
完成或者異常或者cancel
都會被調用;
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;
}
private RunnableFuture<V> newTaskFor(Callable<V> task) {
if (aes == null)
return new FutureTask<V>(task);
else
return aes.newTaskFor(task);
}
若構造器中傳進來的Executor
是AbstractExecutorService
的子類,那么newTaskFor
就會交由子類來決定FutureTask
的類型,達到定制擴展的效果;
void solve(Executor e, Collection<Callable<Result>> solvers)
throws InterruptedException, ExecutionException {
CompletionService<Result> ecs = new ExecutorCompletionService<Result>(e);
for (Callable<Result> s : solvers)
ecs.submit(s);
int n = solvers.size();
for (int i = 0; i < n; ++i) {
Result r = ecs.take().get(); // 這里會拋出中斷異常
if (r != null)
use(r);
}
}
官方提供的用法示例,第一個比較簡單,遍歷任務集合中每個任務執行后獲取結果,是一種順序執行的過程,執行 -> 獲取結果 -> 執行 -> 獲取結果,當然執行任務過程可以是異步的,如果遇到中斷異常會停止任務;