前提概要
在開發過程中在使用多執行緒進行並行處理一些事情的時候,大部分場景在處理多執行緒並行執行任務的時候,可以通過List新增Future來獲取執行結果,有時候我們是不需要獲取任務的執行結果的,方便後面引出ExecutorCompletionService。
CompletionService的介紹
-
CompletionService 介面是一個獨立的介面,並沒有擴充套件ExecutorService 。 其預設實現類是ExecutorCompletionService。
-
介面CompletionService 的功能是:以非同步的方式一邊執行未完成的任務,一邊記錄、處理已完成任務的結果。從而可以將任務的執行與處理任務的執行結果分離開來。
CompletionService的實現原理
-
CompletionService就是監視著 Executor執行緒池執行的任務,用BlockingQueue將完成的任務的結果儲存下來。
-
要不斷遍歷與每個任務關聯的Future,然後不斷去輪詢,判斷任務是否已經完成,功能比較繁瑣。
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;
}
方法摘要
提交一個 Callable 任務;一旦完成,便可以由take()、poll()方法獲取
Future submit(Callable task):
提交一個 Runnable 任務,並指定計算結果;
Future submit(Runnable task, V result):
獲取並移除表示下一個已完成任務的 Future,如果目前不存在這樣的任務,則等待。
Future take() throws InterruptedException
獲取並移除表示下一個已完成任務的 Future,如果不存在這樣的任務,則返回 null。
Future poll()
獲取並移除表示下一個已完成任務的 Future,如果目前不存在這樣的任務,則將等待指定的時間(如果有必要)。
Future poll(long timeout, TimeUnit unit) throws InterruptedException
例子,程式提交了多個任務,但只要有一個任務完成並返回一個非空的結果,並可以忽略掉其餘的任務。
void eample(Executor e, Collection<Callable<Result>> solvers) throws InterruptedException {
CompletionService<Result> completionService = new ExecutorCompletionService<Result>(e);
int n = solvers.size();
List<Future<Result>> futures = new ArrayList<Future<Result>>(n);
Result result = null;
try {
//提交多個任務
for (Callable<Result> s : solvers)
futures.add(completionService.submit(s));
//
for (int i = 0; i < n; ++i) {
try {
//等待獲取一個已經完成的任務
Result r = completionService.take().get();
//判斷返回結果是否為空
if (r != null) {
result = r;
break;
}
} catch (ExecutionException ignore) {}
}
}
finally {
//取消所有任務
for (Future<Result> f : futures)
f.cancel(true);
}
if (result != null)
use(result);
}
ExecutorCompletionService的介紹
-
ExecutorCompletionService內部有一個先進先出的阻塞佇列,用於儲存已經執行完成的Future,通過呼叫它的take方法或poll方法可以獲取到一個已經執行完成的Future,進而通過呼叫Future介面實現類的get方法獲取最終的結果。
-
ExecutorCompletionService實現了CompletionService,內部通過Executor以及BlockingQueue來實現介面提出的規範,ExecutorCompletionService,提交任務後,可以按任務返回結果的先後順序來獲取各任務執行後的結果,該類實現了介面CompletionService
構造方法
- 指定一個Executor來執行任務,儲存完成的任務的完成佇列是LinkedBlockingQueue ;
- Executor由呼叫者傳遞進來,而Blocking可以使用預設的LinkedBlockingQueue,也可以由呼叫者傳遞。
ExecutorCompletionService(Executor executor):
指定了任務執行器Executor和已完成的任務佇列completionQueue
ExecutorCompletionService(Executor executor, BlockingQueue<Future> completionQueue)
實現構造器
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>>();
}
-
該介面定義了一系列方法:提交實現了Callable或Runnable介面的任務,並獲取這些任務的結果。
-
包裝後提交任務的submit()方法,該類還會將提交的任務封裝成QueueingFuture,這樣就可以實現FutureTask.done()方法,以便於在任務執行完畢後,將結果放入阻塞佇列中。
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;
}
QueueingFuture為內部類:
在提交任務時,將任務封裝成QueueingFuture:
private class QueueingFuture extends FutureTask<Void> {
QueueingFuture(RunnableFuture<V> task) {
super(task, null);
this.task = task;
}
protected void done() { completionQueue.add(task); }
private final Future<V> task;
}
其中,done()方法就是在任務執行完畢後,將任務放入佇列中。
-
在呼叫take()、poll()方法時,會從阻塞佇列中獲取Future物件,以取得任務執行的結果。
-
它繼承自 FutureTask,並且重寫了 done 方法,其方法把任務放到我們包裝執行緒池建立的堵塞佇列裡面;就是當任務執行完成後,就會被放到佇列裡面去了。
-
呼叫其take() 方法,就是阻塞等待,等到的一定是能夠獲取的結果的future,然後再呼叫get()方法獲取執行結果;
最後,如果工作中並行處理任務不需要獲取結果的,我們正常使用執行緒池提交就可以,任務技術只要適合工作的業務場景就是好的。