CompletionService是什麼?
它是JUC包中的一個介面類,預設實現類只有一個ExecutorCompletionService。
CompletionService幹什麼的?
它將非同步任務的生成和執行結果的處理進行了解耦,用來執行Callable的任務(實際也是通過Executor執行緒池執行的,只是它又加了一層封裝),我們只需要呼叫它的take(阻塞)/poll(非阻塞)方法便可以獲取到執行完的任務結果,最先獲取到的必定是先執行完的非同步任務結果。
主要應用場景:同時執行多個Callable任務,並且需對任務的返回結果進行處理。若想優先處理先執行完的任務結果,使用它尤其方便。
ExecutorCompletionService 原始碼解析
有三個成員變數,關鍵的是下面標註的變數1、變數2:
1 public class ExecutorCompletionService<V> implements CompletionService<V> { 2 private final Executor executor; // 變數1: 執行緒池 3 private final AbstractExecutorService aes; 4 private final BlockingQueue<Future<V>> completionQueue; // 變數2: 阻塞佇列
兩個構造器,如下,用於初始化上面的三個成員變數,可以看到Executor執行緒池是必傳的:
1 public ExecutorCompletionService(Executor executor) { 2 if (executor == null) 3 throw new NullPointerException(); 4 this.executor = executor; 5 this.aes = (executor instanceof AbstractExecutorService) ? 6 (AbstractExecutorService) executor : null; 7 this.completionQueue = new LinkedBlockingQueue<Future<V>>(); 8 }
1 public ExecutorCompletionService(Executor executor, 2 BlockingQueue<Future<V>> completionQueue) { 3 if (executor == null || completionQueue == null) 4 throw new NullPointerException(); 5 this.executor = executor; 6 this.aes = (executor instanceof AbstractExecutorService) ? 7 (AbstractExecutorService) executor : null; 8 this.completionQueue = completionQueue; 9 }
核心方法submit,把task封裝成一個QueueingFuture,作為執行任務交給執行緒池執行:
1 public Future<V> submit(Callable<V> task) { 2 if (task == null) throw new NullPointerException(); 3 RunnableFuture<V> f = newTaskFor(task); 4 executor.execute(new QueueingFuture(f)); 5 return f; 6 }
下面再來看一下QueueingFuture物件,也是一個核心物件,如下所示。QueueingFuture是ExecutorCompletionService的私有內部類,它重寫了FutureTask的done()方法。當任務執行完成set值的時候,會呼叫done方法,在done方法中將task存入阻塞佇列。先執行完的任務就會先放入阻塞佇列,所以我們從佇列中取結果的時候,必定是先取到先執行完的任務。
1 private class QueueingFuture extends FutureTask<Void> { 2 QueueingFuture(RunnableFuture<V> task) { 3 super(task, null); 4 this.task = task; 5 } 6 protected void done() { completionQueue.add(task); } 7 private final Future<V> task; 8 }
總結一下,ExecutorCompletionService是通過QueueingFuture的done方法和阻塞佇列實現的按照非同步任務返回順序來返回結果。
ExecutorCompletionService和ExecutorService的使用demo示例
Callable類:
1 class CsCallable implements Callable<String> { 2 private String name; 3 private long milli; 4 5 public CsCallable(String name, long milli) { 6 this.name = name; 7 this.milli = milli; 8 } 9 10 @Override 11 public String call() throws Exception { 12 System.out.println("name:" + name); 13 Thread.sleep(milli); 14 return name + " after " + milli + "ms call back."; 15 } 16 }
ExecutorCompletionService的demo:
1 public class CompletionServiceDemo { 2 public static void main(String[] args) throws Exception { 3 CompletionService completionService = new ExecutorCompletionService(Executors.newFixedThreadPool(4)); 4 completionService.submit(new CsCallable("xxx", 5000)); 5 completionService.submit(new CsCallable("www", 2000)); 6 completionService.submit(new CsCallable("zzz", 14000)); 7 completionService.submit(new CsCallable("yyy", 9000)); 8 for (int i = 0; i < 4; i++) { 9 System.out.println(completionService.take().get()); 10 } 11 System.out.println("----- main over -----"); 12 } 13 }
執行結果如下,可以看到早完成的任務結果能先獲取到:
ExecutorService的demo:
1 public class ExecutorServiceDemo { 2 public static void main(String[] args) throws Exception { 3 ExecutorService executorService = Executors.newFixedThreadPool(4); 4 List<Future<String>> list = new ArrayList<>(); 5 list.add(executorService.submit(new CsCallable("xxx", 5000))); 6 list.add(executorService.submit(new CsCallable("www", 2000))); 7 list.add(executorService.submit(new CsCallable("zzz", 14000))); 8 list.add(executorService.submit(new CsCallable("yyy", 9000))); 9 for (Future<String> future : list) { 10 System.out.println(future.get()); 11 } 12 System.out.println("----- main over -----"); 13 } 14 }
執行結果如下,只能按照指定的順序處理返回結果,無法先處理早完成的任務: