当我们通过Executor提交一组并发执行的任务,并且希望在每一个任务完成后能立即得到结果,有两种方式可以采取:

方式一:

通过一个list来保存一组future,然后在循环中轮训这组future,直到每个future都已完成。如果我们不希望出现因为排在前面的任务阻塞导致后面先完成的任务的结果没有及时获取的情况,那么在调用get方式时,需要将超时时间设置为0

 public class ExecutorCompletionServiceTest {

     static class Task implements Callable<String> {
private int i; public Task(int i) {
this.i = i;
} @Override
public String call() throws Exception {
Thread.sleep(10000);
return Thread.currentThread().getName() + "执行完任务:" + i;
}
} public static void main(String[] args) {
testUseFuture();
} private static void testUseFuture() {
int numThread = 5;
ExecutorService executor = Executors.newFixedThreadPool(numThread);
List<Future<String>> futureList = new ArrayList<Future<String>>();
for (int i = 0; i < numThread; i++) {
Future<String> future = executor
.submit(new ExecutorCompletionServiceTest.Task(i));
futureList.add(future);
} while (numThread > 0) {
for (Future<String> future : futureList) {
String result = null;
try {
result = future.get(0, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
// 超时异常直接忽略
}
if (null != result) {
futureList.remove(future);
numThread--;
System.out.println(result);
// 此处必须break,否则会抛出并发修改异常。(也可以通过将futureList声明为CopyOnWriteArrayList类型解决)
break;
}
}
}
}
}

方式二:

第一种方式显得比较繁琐,通过使用ExecutorCompletionService,则可以达到代码最简化的效果。

 public class ExecutorCompletionServiceTest {

     static class Task implements Callable<String> {
private int i; public Task(int i) {
this.i = i;
} @Override
public String call() throws Exception {
Thread.sleep(10000);
return Thread.currentThread().getName() + "执行完任务:" + i;
}
} public static void main(String[] args) throws InterruptedException, ExecutionException {
testExecutorCompletionService();
} private static void testExecutorCompletionService() throws InterruptedException, ExecutionException{
int numThread = 5;
ExecutorService executorService = Executors.newFixedThreadPool(numThread);
CompletionService<String> completionService = new ExecutorCompletionService<>(executorService);
for (int i = 0; i < numThread; i++) {
completionService.submit(new ExecutorCompletionServiceTest.Task(i));
}
for (int i = 0; i < numThread; i++) {
System.out.println(completionService.take().get());
}
executorService.shutdown();
}
}

ExecutorCompletionService实现了CompletionService接口,CompletionService是Executor和BlockingQueue的结合体。可以看下构造函数

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

任务的提交和执行都是委托给Executor来完成。当提交某个任务时,该任务首先将被包装为一个QueueingFuture,

 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是FutureTask的一个子类,通过改写该子类的done方法,可以实现当任务完成时,将结果放入到BlockingQueue中。

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

而通过使用BlockingQueue的take或poll方法,则可以得到结果。在BlockingQueue不存在元素时,这两个操作会阻塞,一旦有结果加入,则立即返回。

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

ExecutorCompletionService分析及使用的更多相关文章

  1. 获取Executor提交的并发执行的任务返回结果的两种方式/ExecutorCompletionService使用

    当我们通过Executor提交一组并发执行的任务,并且希望在每一个任务完成后能立即得到结果,有两种方式可以采取: 方式一: 通过一个list来保存一组future,然后在循环中轮训这组future,直 ...

  2. Future 和 ExecutorCompletionService 对比和使用

    当我们通过Executor提交一组并发执行的任务,并且希望在每一个任务完成后能立即得到结果,有两种方式可以采取: 方式一: 通过一个list来保存一组future,然后在循环中轮训这组future,直 ...

  3. java并发包java.util.concurrent详解

    线程池ThreadPoolExecutor的使用 并发容器之CopyOnWriteArrayList 并发容器之CopyOnWriteArraySet 数据结构之ConcurrentHashMap,区 ...

  4. CompletionService用法踩坑解决优化

    转自:https://blog.csdn.net/xiao__miao/article/details/86352380 1.近期工作的时候,运维通知一个系统的内存一直在增长,leader叫我去排查, ...

  5. java.util.concurrent包详细分析--转

    原文地址:http://blog.csdn.net/windsunmoon/article/details/36903901 概述 Java.util.concurrent 包含许多线程安全.测试良好 ...

  6. Solr初始化源码分析-Solr初始化与启动

    用solr做项目已经有一年有余,但都是使用层面,只是利用solr现有机制,修改参数,然后监控调优,从没有对solr进行源码级别的研究.但是,最近手头的一个项目,让我感觉必须把solrn内部原理和扩展机 ...

  7. JDK并发包中ExecutorCompletionService使用

    相信大家都知道,jdk中ExecutorService是并发编程时使用很频繁的接口,并且使用很方便,那么想在有这么一个场景: 一批任务使用线程池处理,并且需要获得结果,但是不关心任务执行结束后输出结果 ...

  8. JDK源码分析之concurrent包(三) -- Future方式的实现

    上一篇我们基于JDK的源码对线程池ThreadPoolExecutor的实现做了分析,本篇来对Executor框架中另一种典型用法Future方式做源码解读.我们知道Future方式实现了带有返回值的 ...

  9. JUC源码分析-线程池篇(二)FutureTask

    JUC源码分析-线程池篇(二)FutureTask JDK5 之后提供了 Callable 和 Future 接口,通过它们就可以在任务执行完毕之后得到任务的执行结果.本文从源代码角度分析下具体的实现 ...

随机推荐

  1. Leetcode_121_Best Time to Buy and Sell Stock

    本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/43024967 Say you have an array ...

  2. Oracle EBS BOM模块常用表结构

    表名: bom.bom_bill_of_materials  说明: BOM清单父项目  BILL_SEQUENCE_ID NUMBER 清单序号(关键字)ASSEMBLY_ITEM_ID NUMBE ...

  3. 中文注释 MariaDB my.cnf 大型服务器配置模板

    文件如下所示,请根据需要进行修改: 翻译日期: 2014年5月22日 翻译人员: 铁锚 # /usr/share/mysql/my-huge.cnf # MariaDB 配置文件 示例模板(huge, ...

  4. ITU-T G.1080 IPTV的体验质量(QoE)要求 (Quality of experience requirements for IPTV services)

    IPTV的服务质量(QoE)要求 Quality of experience requirements for IPTV services Summary This Recommendation de ...

  5. 嵌入式C语言位运算之清位置位

    如题,在嵌入式开发中,掌握位运算是节省开发时间和提高开发效率的一种高效方式. 我们不得不去熟悉如何快速掌握位运算这种高效的技巧,接下来看看程序.. #include <stdio.h> # ...

  6. 本人在CSDN上的技术博客访问量突破了10万次,特此截图留念

                从 2011-11-16在CSDN开博至今,将近三年.  在近三年的时间里,本博的访问量于2014-07-01突破了10万次,单篇博文<软件开发高手须掌握的4大SQL精髓 ...

  7. 面试之路(10)-BAT面试之java实现单链表的插入和删除

    链表的结构: 链表在空间是不连续的,包括: 数据域(用于存储数据) 指针域(用于存储下一个node的指针) 单项链表的代码实现: 节点类 构造函数 数据域的get,set方法 指针域的get,set方 ...

  8. leetcode之旅(6)-Add Digits

    题目: Given a non-negative integer num, repeatedly add all its digits until the result has only one di ...

  9. SpringBoot yml 配置

    1. 在 spring boot 中,有两种配置文件,一种是application.properties,另一种是application.yml,两种都可以配置spring boot 项目中的一些变量 ...

  10. CKEditor5 + ckfinder3(php)

    CKEditor5资源下载,这里我们选择ckeditor5-build-classic下载: https://ckeditor.com/ckeditor-5-builds/download/ ckfi ...