什么是CompletionService?

当我们使用ExecutorService启动多个Callable时,每个Callable返回一个Future,而当我们执行Future的get方法获取结果时,可能拿到的Future并不是第一个执行完成的Callable的Future,就会进行阻塞,从而不能获取到第一个完成的Callable结果,那么这样就造成了很严重的性能损耗问题。

而CompletionService正是为了解决这个问题,它是Java8的新增接口,它的实现类是ExecutorCompletionService。CompletionService会根据线程池中Task的执行结果按执行完成的先后顺序排序,任务先完成的可优先获取到。

ExecutorCompletionService中的方法

构造方法

构建ExecutorCompletionService对象

  • executor:关联的线程池

  • completionQueue:自定义的结果存储队列

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

submit方法

提交一个Callable或者Runnable类型的任务,并返回Future

Future<V> submit(Callable<V> task)
Future<V> submit(Runnable task, V result)

take方法

阻塞方法,从结果队列中获取并移除一个已经执行完成的任务的结果,如果没有就会阻塞,直到有任务完成返回结果。

Future<V> take() throws InterruptedException

poll方法

从结果队列中获取并移除一个已经执行完成的任务的结果,如果没有就会返回null,该方法不会阻塞

  • timeout:最多等待多长时间

  • unit:时间单位

  Future<V> poll()
Future<V> poll(long timeout, TimeUnit unit)

案例

问题复现

不使用CompletionService时出现的问题

package com.brycen.part3.threadpool;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*; public class CompletionServiceExample {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newFixedThreadPool(2); List<Callable<Integer>> callables = Arrays.asList(
()->{
mySleep(20);
System.out.println("=============20 end==============");
return 20;
},
()->{
mySleep(10);
System.out.println("=============10 end==============");
return 10;
}
); List<Future<Integer>> futures = new ArrayList<>();
//提交任务,并将future添加到list集合中
futures.add(executorService.submit(callables.get(0)));
futures.add(executorService.submit(callables.get(1)));
//遍历Future,因为不知道哪个任务先完成,所以这边模拟第一个拿到的就是执行时间最长的任务,那么执行时间较短的任务就必须等待执行时间长的任务执行完
for (Future future:futures) {
System.out.println("结果: "+future.get());
}
System.out.println("============main end=============");
}
private static void mySleep(int seconds){
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

运行结果:

  • 即使休眠10秒的任务先执行完成也不会输出结果,因为在拿结果的时候可能先拿的休眠20秒的任务的结果,而休眠20秒的任务还没有执行完,此时就会阻塞住,从而影响了性能。
=============10 end==============
=============20 end==============
结果: 20
结果: 10
============main end=============

利用CompletionService解决问题

package com.brycen.part3.threadpool;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*; public class CompletionServiceExample {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newFixedThreadPool(2); List<Callable<Integer>> callables = Arrays.asList(
()->{
mySleep(20);
System.out.println("=============20 end==============");
return 20;
},
()->{
mySleep(10);
System.out.println("=============10 end==============");
return 10;
}
); //构建ExecutorCompletionService,与线程池关联
CompletionService completionService = new ExecutorCompletionService(executorService);
//提交Callable任务
completionService.submit(callables.get(0));
completionService.submit(callables.get(1)); //获取future结果,不会阻塞
Future<Integer> pollFuture = completionService.poll();
//这里因为没有执行完成的Callable,所以返回null
System.out.println(pollFuture);
//获取future结果,最多等待3秒,不会阻塞
Future<Integer> pollTimeOutFuture = completionService.poll(3,TimeUnit.SECONDS);
//这里因为没有执行完成的Callable,所以返回null
System.out.println(pollTimeOutFuture);
//通过take获取Future结果,此方法会阻塞
for(int i=0;i<callables.size();i++){
System.out.println(completionService.take().get());
} System.out.println("============main end=============");
}
private static void mySleep(int seconds){
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

运行结果:

null
null
=============10 end==============
10
=============20 end==============
20
============main end=============

文档:https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CompletionService.html

【Java多线程】CompletionService的更多相关文章

  1. 【转】 Java 多线程之一

    转自   Java 多线程 并发编程 一.多线程 1.操作系统有两个容易混淆的概念,进程和线程. 进程:一个计算机程序的运行实例,包含了需要执行的指令:有自己的独立地址空间,包含程序内容和数据:不同进 ...

  2. (转载)Java多线程入门理解

    转载出处http://blog.csdn.net/evankaka 写在前面的话:此文只能说是java多线程的一个入门,其实Java里头线程完全可以写一本书了,但是如果最基本的你都学掌握好,又怎么能更 ...

  3. Java多线程学习开发笔记

    线程有有序性和可见性 多个线程之间是不能直接传递数据交互的,它们之间的交互只能通过共享变量来实现. 在多个线程之间共享类的一个对象,这个对象是被创建在主内存(堆内存)中,每个线程都有自己的工作内存(线 ...

  4. java多线程管理 concurrent包用法详解

        我们都知道,在JDK1.5之前,Java中要进行业务并发时,通常需要有程序员独立完成代码实现,当然也有一些开源的框架提供了这些功能,但是这些依然没有JDK自带的功能使用起来方便.而当针对高质量 ...

  5. java多线程系列六、线程池

    一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池. 2. 使用线程池的好处 a) 降低资源的消耗.使用线程池不用频繁的创建线程和销毁线程 b) 提高响应速度,任 ...

  6. Java多线程学习(吐血超详细总结)(转)

    林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 写在前面的话:此文只能说是java多线程的一个入门,其实Java里头线程完全可以写一本书了,但 ...

  7. (转)Java多线程学习(吐血超详细总结)

    本文转自:http://blog.csdn.net/evankaka 写在前面的话:此文只能说是java多线程的一个入门,其实Java里头线程完全可以写一本书了,但是如果最基本的你都学掌握好,又怎么能 ...

  8. Java多线程学习(吐血超具体总结)

    林炳文Evankaka原创作品. 转载请注明出处http://blog.csdn.net/evankaka 写在前面的话:此文仅仅能说是java多线程的一个入门.事实上Java里头线程全然能够写一本书 ...

  9. Java多线程编程实战指南(核心篇)读书笔记(五)

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76730459冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...

  10. Java 多线程 2015/9/21

    http://lavasoft.blog.51cto.com/62575/27069   http://blog.csdn.net/aboy123/article/details/38307539   ...

随机推荐

  1. 一、Windows部署RabbitMQ

    RabbitMQ官方网站非常详细,以下只是本人学习过程的整理 一.Windows部署RabbitMQ:https://www.cnblogs.com/yangleiyu/p/15539618.html ...

  2. Git 修改已提交的commit注释

    两种情况: 1.已经将代码push到远程仓库 2.还没将代码push到远程仓库,还在本地的仓库中 这两种情况下的修改大体相同,只是第一种情况最后会多一步 下面来说怎么修改 先搞清楚你要修改哪次的提交注 ...

  3. PHP、TP6框架及JavaScript的单步调试

    目录 一.PHP程序的调试 1. 单个PHP程序的调试 2. PHP框架代码的调试 二.JavaScript程序的调试 三.总结 参考资料:https://www.bilibili.com/video ...

  4. Python知识整理(三)

    三.函数式编程与模块 1.函数式编程 1.高阶函数 把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式. 1.map/reduce map()函数接收两个参数,一个是函 ...

  5. js实现全选与全部取消功能

    function checkAll() { //把所有参与选择的checkbox使用相同的name,这里为"num_iid"    var eles = document.getE ...

  6. 字符编码和python文件操作

    字符编码和文件操作 目录 字符编码和文件操作 1. 字符编码 1.1 什么是字符编码 1.2 字符编码的发展史 1.2.1 ASCII码 1.2.2 各国编码 1.2.3 Unicode 1.3 字符 ...

  7. [cf1458D]Flip and Reverse

    将$s$中的01分别变为$1,-1$,即得到一个序列$a_{i}$(设其长度为$n$,下标范围为$[1,n]$) 对$a_{i}$建立一张有向图,其点集合为$Z$,并对$\forall 0\le k& ...

  8. MS17-010漏洞利用

    MS17-010漏洞利用 1.安装虚拟机win7 x64,实现利用ms17-010实现对其win7 x64主机开始渗透,查看该主机信息,打开远程桌面,抓取用户名和密码并破译,创建一个 : 学号.txt ...

  9. Jetpack架构组件学习(2)——ViewModel和Livedata使用

    要看本系列其他文章,可访问此链接Jetpack架构学习 | Stars-One的杂货小窝 原文地址:Jetpack架构组件学习(2)--ViewModel和Livedata使用 | Stars-One ...

  10. Nginx 动态增加扩展

    Nginx 动态增加扩展 1. 先查看目前nginx已加载模块 /home/nginx-1.18.0 # nginx -V nginx version: nginx/1.18.0 built by g ...