什么是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. ant生成jmeter测试报告没有数据【已解决】

    1.如下图,在配置build时,到网上找配置文件,配置样式表文件名经常是jmeter-results-detail-report_21.xsl, 3.但是在其他版本可能被不是这个文件名,在jmeter ...

  2. AppGallery Connect场景化开发实战—注册订阅通知

    借助AppGallery Connect(以下简称AGC)的认证服务,云函数,短信服务等服务,当用户注册成功后,便可以在注册的手机号或者邮箱地址中收到一条应用的欢迎短信或者欢迎邮件.以便让开发者更快地 ...

  3. .Net Core微服务——Ocelot(3):超时、熔断、限流

    基本概念 超时.熔断.限流听起来好像很远,但实际上用在方方面面.很多人可能还搞不懂熔断是做什么,其实可以把熔断理解为一种防护措施.做个假设,在微服务体系下,某个下游服务响应很慢,然后随着时间推移,会有 ...

  4. 【Microsoft Azure 的1024种玩法】一.一分钟快速上手搭建宝塔管理面板

    简介 宝塔Linux面板是提升运维效率的服务器管理软件,其支持一键LAMP/LNMP/集群/监控/网站/FTP/数据库/JAVA等100多项服务器管理功能.今天带大家一起学习的内容为一分钟快速上手搭建 ...

  5. [loj3076]公园

    为了方便,对题意做以下处理: 1.称"西部主题"和"科幻主题"分别为黑色和白色 2.删去题中"保证没有两条不同的道路连接同一对景点"的条件 ...

  6. [hdu6581]Vacation

    首先发现,最终第0辆车一定被堵在某一辆车前,那么等价于它的初始位置就在(那辆车的位置+中间车的车长)/那辆车的速度,其中最大的那个就是答案因此得出结论:$ans=max((\sum_{j=1}^{i} ...

  7. [uoj272]石家庄的工人阶级队伍比较坚强

    假设$x,y\in \{0,1,2\}$,则$x$能赢$y$(根据题中定义)当且仅当$x-y\equiv 1(mod\ 3)$ 定义$\ominus$为两数3进制下不退位的减法,$S_{x}$表示$x ...

  8. [bzoj4553]序列

    记第i个位置有三个属性:1.ai表示原来的值:2.bi表示变成最大的值:3.ci表示变成最小的值.那么对于如果i在j的前面,那么必然有:$ai\le cj$且$bi\le aj$,那么令f[i]表示以 ...

  9. go语言并发编程

    引言 说到go语言最厉害的是什么就不得不提到并发,并发是什么?,与并发相关的并行又是什么? 并发:同一时间段内执行多个任务 并行:同一时刻执行多个任务 进程.线程与协程 进程: 进程是具有一定独立功能 ...

  10. 你真的知道Spring注解驱动的前世今生吗?这篇文章让你豁然开朗!

    本篇文章,从Spring1.x到Spring 5.x的迭代中,站在现在的角度去思考Spring注解驱动的发展过程,这将有助于我们更好的理解Spring中的注解设计. Spring Framework ...