1、在异步任务进程中,一种常见的场景是,主线程提交多个异步任务,然后希望有任务完成就处理结果,并且按任务完成顺序逐个处理,对于这种场景,Java 并发包提供了一个方便的方法,使用 CompletionService,这是一个接口,它的实现类是 ExecutorCompletionService。

2、与 ExecutorService 一样,CompletionService 也可以提交异步任务,它的不同是,它可以按任务完成顺序获取结果,其具体定义为:

1
2
3
4
5
6
7
public interface <> {
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;
}

submit 方法与 ExecutorService 是一样的,多了 take 和 poll 方法,它们都是获取下一个完成任务的结果,take() 会阻塞等待,poll() 会立即返回,如果没有已完成的任务,返回 null,带时间参数的 poll 方法会最多等待限定的时间。

2、CompletionService 的主要实现类是 ExecutorCompletionService,它依赖于一个 Executor 完成实际的任务提交,而自己主要负责结果的排队和处理。它的构造方法有两个:

1
2
public ExecutorCompletionService(Executor executor)
public ExecutorCompletionService(Executor executor, BlockingQueue<Future<V>> completionQueue)

至少需要一个 Executor 参数,可以提供一个 BlockingQueue 参数,用作完成任务的队列,没有提供的话,ExecutorCompletionService 内部会创建一个 LinkedBlockingQueue。

3、ExecutorCompletionService 是怎么让结果有序处理的呢?
因为它有一个额外的队列,每个任务完成之后,都会将代表结果的 Future 入队。在 FutureTask 中,任务完成后,不管是正常完成、异常结束、还是被取消,都会调用 finishCompletion 方法,而该方法会调用一个 done 方法 protected void done() { } 该方法的实现为空,但它是一个 protected 方法,子类可以重写该方法。ExecutorCompletionService 的内部类 QueueingFuture 中重写了该方法。

在 ExecutorCompletionService 中,提交的任务类型不是一般的 FutureTask,而是一个子类 QueueingFuture

大专栏  Java 线程总结(十四)>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 final BlockingQueue<Future<V>> completionQueue;

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;
}
---------------------------

而 ExecutorCompletionService 的 take/poll 方法就是从该队列获取结果:

1
2
3
public Future<V> take() throws InterruptedException {
return completionQueue.take();
}

4、AbstractExecutorService 的 invokeAny 的实现,就利用了 ExecutorCompletionService,它的基本思路是,提交任务后,通过 take 方法获取结果,获取到第一个有效结果后,取消所有其他任务。

5、CompletionService 它通过一个额外的结果队列,方便了对于多个异步任务结果的处理。

参考博客

Java编程的逻辑 - 方便的 CompletionService

Java 线程总结(十四)的更多相关文章

  1. “全栈2019”Java多线程第十四章:线程与堆栈详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  2. “全栈2019”Java多线程第二十四章:等待唤醒机制详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  3. Java进阶(三十四)Integer与int的种种比较你知道多少?

    Java进阶(三十四)Integer与int的种种比较你知道多少? 前言 如果面试官问Integer与int的区别:估计大多数人只会说到两点:Ingeter是int的包装类,注意是一个类:int的初值 ...

  4. 《Java程序设计》十四次作业

    <Java程序设计>十四次作业实验总结 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结与数据库相关内容. 3. 代码量统计 周次 总代码量 新增代码量 总文件数 新增 ...

  5. “全栈2019”Java异常第十四章:将异常输出到文本文件中

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...

  6. “全栈2019”Java第八十四章:接口中嵌套接口详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  7. “全栈2019”Java第七十四章:内部类与静态内部类相互嵌套

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  8. “全栈2019”Java第六十四章:接口与静态方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  9. “全栈2019”Java第五十四章:多态详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

随机推荐

  1. dockerfile保留字指令

    FROM 基础镜像,当前新镜像是基于哪个镜像的 MAINTAINER 镜像维护者的姓名和邮箱地址 RUN 容器构建时运行的命令 EXPOSE 当前容器对外暴露的端口 WORKDIR 指定在创建容器后, ...

  2. C++之namespace、bool

    namespace: 1.namespace:标识符的各种可见范围.C++ 标准程序库中的所有标识符都被定义在一个名为  std 的namespace中. 2.当使用<iostream>的 ...

  3. 长沙中考2019数学T25讲解

    好久没更Blog了... 为了应付完成寒假作业,还是更一下(再不更都庚子年了) Upd:2020.1.22 题目 第一问 还是比较水友好的 给顶点就相当于多给了对称轴-\(\frac{b}{2a}\) ...

  4. 时间API

    1. 时间API 我们的时间在java里是long类型的整数,这个整数称之为时间戳(也叫格林威治时间),即从1970-01-01到现在为止所经过的毫秒数,单有这个时间戳是不能准确表达世界各地的时间,还 ...

  5. 如何选字体(font-family)

    一.默认字体情况 1.Window下: 宋体(SimSun):Win下大部分游览器的默认字体,宋体在小字号下(如12px.14px)的显示效果还可以接受,但是字号一大就非常糟糕了,所以使用的时候要注意 ...

  6. 给select赋值之后,再点击选择下拉值时,显示一值不变的解决

    在一个项目需求中,请求数据,得到的数据dataAll渲染到页面的select下拉表单中,当时是需要一进页面就要默认选中第一个选项,所以直接将dataAll的第一个索引值赋值给了表单的绑定值formVa ...

  7. Java常用的公共方法

    --获取规字符串中的指定名称的某个字段值 1.public String getValueByName(String params,String name) --用于通过表单选中的复选框获取它的值(j ...

  8. 如何保存Keras模型

    我们不推荐使用pickle或cPickle来保存Keras模型 你可以使用model.save(filepath)将Keras模型和权重保存在一个HDF5文件中,该文件将包含: 模型的结构,以便重构该 ...

  9. shell的集合运算

    用cat,sort,uniq命令实现文件行的交集 .并集.补集 交集 $F_1 \cap F_2 $ cat f1 f2 | sort | uniq -d 并集 $F_1 \cup F_2 $ cat ...

  10. maven仓库镜像、私服与jdk版本配置

    --配置全局镜像,setting.xml <mirrors> <mirror> <id>alimaven</id> <name>aliyun ...