• Java并发之CompletionService详解


    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 }

    执行结果如下,只能按照指定的顺序处理返回结果,无法先处理早完成的任务

  • 相关阅读:
    X oracle 12c new feature: Automatic Report Capturing Feature
    X 搭建Postgresql configure: error: readline library not found
    X windows上利用vmvare搭建共享存储搭建rac
    洛谷P1397 [NOI2013]矩阵游戏
    BSOJ 6289【NOIP2018模拟赛】黄昏
    BSOJ 4282 秀秀的照片
    BSOJ 5185【11.08题目】暴力破解
    BSOJ 5100 简单的区间
    VUE 项目自适应屏幕和浏览器
    yarn npm区别
  • 原文地址:https://www.cnblogs.com/zzq6032010/p/14444243.html
Copyright © 2020-2023  润新知