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 }

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

Java并发之CompletionService详解的更多相关文章

  1. Java并发之AQS详解

    一.概述 谈到并发,不得不谈ReentrantLock:而谈到ReentrantLock,不得不谈AbstractQueuedSynchronizer(AQS)! 类如其名,抽象的队列式的同步器,AQ ...

  2. Java并发之AQS详解(转)

    一.概述 谈到并发,不得不谈ReentrantLock:而谈到ReentrantLock,不得不谈AbstractQueuedSynchronized(AQS)! 类如其名,抽象的队列式的同步器,AQ ...

  3. Java并发之ReentrantLock详解

    一.入题 ReentrantLock是Java并发包中互斥锁,它有公平锁和非公平锁两种实现方式,以lock()为例,其使用方式为: ReentrantLock takeLock = new Reent ...

  4. java并发之CAS详解

    前言 在高并发的应用当中,最关键的问题就是对共享变量的安全访问,通常我们都是通过加锁的方式,比如说synchronized.Lock来保证原子性,或者在某些应用当中,用voliate来保证变量的可见性 ...

  5. java并发之synchronized详解

    前言 多个线程访问同一个类的synchronized方法时, 都是串行执行的 ! 就算有多个cpu也不例外 ! synchronized方法使用了类java的内置锁, 即锁住的是方法所属对象本身. 同 ...

  6. Android开发之InstanceState详解

    Android开发之InstanceState详解   本文介绍Android中关于Activity的两个神秘方法:onSaveInstanceState() 和 onRestoreInstanceS ...

  7. Android开发之InstanceState详解(转)---利用其保存Activity状态

    Android开发之InstanceState详解   本文介绍Android中关于Activity的两个神秘方法:onSaveInstanceState() 和 onRestoreInstanceS ...

  8. Android开发之MdiaPlayer详解

    Android开发之MdiaPlayer详解 MediaPlayer类可用于控制音频/视频文件或流的播放,我曾在<Android开发之基于Service的音乐播放器>一文中介绍过它的使用. ...

  9. Java 8 Stream API详解--转

    原文地址:http://blog.csdn.net/chszs/article/details/47038607 Java 8 Stream API详解 一.Stream API介绍 Java8引入了 ...

随机推荐

  1. 23.centos 7配置网络

    1.ifconfig:查看网卡信息   如果centos7 最小化安装没有ifconfig这个命令,可以使用yum install net-tools 来安装. centos7 网卡命名规则:  en ...

  2. TCP/IP__TCP协议常用协议默认端口号

  3. Codeforces Round #697 (Div. 3) D. Cleaning the Phone (思维,前缀和)

    题意:你的手机有\(n\)个app,每个app的大小为\(a_i\),现在你的手机空间快满了,你需要删掉总共至少\(m\)体积的app,每个app在你心中的珍惜值是\(b_i\),\(b_i\)的取值 ...

  4. HDU -1506 Largest Rectangle in a Histogram&&51nod 1158 全是1的最大子矩阵 (单调栈)

    单调栈和队列讲解:传送门 HDU -1506题意: 就是给你一些矩形的高度,让你统计由这些矩形构成的那个矩形面积最大 如上图所示,如果题目给出的全部是递增的,那么就可以用贪心来解决 从左向右依次让每一 ...

  5. hdu5375 Gray code

    Problem Description The reflected binary code, also known as Gray code after Frank Gray, is a binary ...

  6. hdu3033 I love sneakers!

    Problem Description After months of hard working, Iserlohn finally wins awesome amount of scholarshi ...

  7. 3.PowerShell DSC核心概念

    PowerShell DSC有三个核心概念 配置 配置是声明性的PowerShell 脚本,用于定义和配置资源实例. DSC 配置是幂等的. 资源 资源是 DSC 的"实现器"部分 ...

  8. Numpy Quickstart tutorial

    此文是关于Numpy的一些基本用法, 内容来源于Numpy官网:https://docs.scipy.org/doc/numpy-dev/user/quickstart.html 1.The Basi ...

  9. tensorflow报错:Attempting to fetch value instead of handling error Internal: failed to get device attribute 13 for device 0: CUDA_ERROR_UNKNOWN:

    就是在spyder跑上一篇文章的代码然后就报错: Attempting to fetch value instead of handling error Internal: failed to get ...

  10. 攻防世界-Web-lottery(.git泄露、php源码审计、弱类型利用)

    扫描目录,发现.git泄露: 提取.git泄露的源码,得到许多文件: 网站这里: 这就要审计一下代码,找找漏洞了. 经过一番审计,猜数字对应的函数在api.php中: 我们要绕过这个$win_numb ...