我们现在在Java中使用多线程通常不会直接用Thread对象了,而是会用到java.util.concurrent包下的ExecutorService类来初始化一个线程池供我们使用。

之前我一直习惯自己维护一个list保存submit的callable task所返回的Future对象。

在主线程中遍历这个list并调用Future的get()方法取到Task的返回值。

public class CompletionServiceTest {

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

  

但是,我在很多地方会看到一些代码通过CompletionService包装ExecutorService,然后调用其take()方法去取Future对象。

public class CompletionServiceTest {

	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 executor = Executors.newFixedThreadPool(numThread);
CompletionService<String> completionService = new ExecutorCompletionService<String>(executor);
for(int i = 0;i<numThread;i++ ){
completionService.submit(new CompletionServiceTest.Task(i));
}
} for(int i = 0;i<numThread;i++ ){
System.out.println(completionService.take().get());
} }

  

以前没研究过这两者之间的区别。今天看了源代码之后就明白了。

这两者最主要的区别在于submit的task不一定是按照加入自己维护的list顺序完成的。

从list中遍历的每个Future对象并不一定处于完成状态,这时调用get()方法就会被阻塞住,如果系统是设计成每个线程完成后就能根据其结果继续做后面的事,这样对于处于list后面的但是先完成的线程就会增加了额外的等待时间。

而CompletionService的实现是维护一个保存Future对象的BlockingQueue。只有当这个Future对象状态是结束的时候,才会加入到这个Queue中,take()方法其实就是Producer-Consumer中的Consumer。它会从Queue中取出Future对象,如果Queue是空的,就会阻塞在那里,直到有完成的Future对象加入到Queue中。

所以,先完成的必定先被取出。这样就减少了不必要的等待时间

Java中ExecutorService和CompletionService区别的更多相关文章

  1. ExecutorService和CompletionService区别

    ExecutorService和CompletionService区别: ExecutorService:一直习惯自己维护一个list保存submit的callable task所返回的Future对 ...

  2. java中ArrayList 、LinkList区别

    转自:http://blog.csdn.net/wuchuanpingstone/article/details/6678653 个人建议:以下这篇文章,是从例子说明的方式,解释ArrayList.L ...

  3. java 中 ==和equals 的区别

      Java中equals和==的区别 java中的数据类型,可分为两类: 1.基本数据类型,也称原始数据类型.byte,short,char,int,long,float,double,boolea ...

  4. java中equals和==的区别 (转)

    java中equals和==的区别  值类型是存储在内存中的堆栈(以后简称栈),而引用类型的变量在栈中仅仅是存储引用类型变量的地址,而其本身则存储在堆中. ==操作比较的是两个变量的值是否相等,对于引 ...

  5. 【转】Java中equals和==的区别

    [转]Java中equals和==的区别 java中的数据类型,可分为两类: 1.基本数据类型,也称原始数据类型.byte,short,char,int,long,float,double,boole ...

  6. java中a++与++a区别

    java中a++与++a区别 a++与++a的区别,如果单独使用没有任何区别,如果在运算中就有区别了,a++是先运算在赋值,而++a是先赋值在运算!! 先看a++的代码哦 class demo1 { ...

  7. c#与java中byte字节的区别及转换方法

    原文:c#与java中byte字节的区别及转换方法 在java中  byte的范围在 [-128,127] 在C#中  byte的范围在 [0,255] 所以 java程序与C#程序 进行数据传输的时 ...

  8. java中==与equel的区别

    值类型是存储在内存中的堆栈(以后简称栈),而引用类型的变量在栈中仅仅是存储引用类型变量的地址,而其本身则存储在堆中. ==操作比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量在堆中存储的地 ...

  9. Java中的“&”和“&&”的区别

    Java中的"&"和"&&"的区别 1."&"是位运算符,"&&"是逻辑 ...

随机推荐

  1. mysql组合索引顺序参考

    问题背景 : 当我们需要创建一个组合索引, 索引的顺序对于效率影响很大, 怎么确定索引的顺序; 解决方法 : 我们应该依据字段的全局基数和选择性, 而不是字段的某个具体的值来确定; 表结构 :  dc ...

  2. smaller programs should improve performance

    COMPUTER ORGANIZATION AND ARCHITECTURE DESIGNING FOR PERFORMANCE NINTH EDITION In this section, we l ...

  3. Redis主从在线互相切换

    由于某些原因,我们可能需要将redis master更换机器,我们可以停机进行更换,但是那样可能影响到用户体验.本文简要操作进行不停机迁移. 系统环境 CentOS 6.3 x64redis-serv ...

  4. ubuntu dpkg 依赖问题处理

    ubuntu dpkg 依赖问题处理 使用 apt-get 安装软件期间,如果出现意外中断的情况,下次安装时会出现 dpkg 的一系列依赖问题,提示如下 :: dpkg: error processi ...

  5. Range对象理解,浏览器兼容性,获取鼠标光标位置

    一.关于浏览器的兼容性 目前主要有3种关于range的类似的对象,分别是W3C range 对象,Mozzlia selection ,ie TextRange 关于这三种的区别,请查看文档 http ...

  6. Nginx下配置SSL安全协议

    生成证书: # cd /usr/local/nginx/conf # openssl genrsa -des3 -out server.key 1024 # openssl req -new -key ...

  7. C#异步:实现一个最简单的异步

    异步就是方法异步执行, 这个好理解. 异步有啥用? 以前只是听说过, 也不想计较. 不过还是碰到了需要这个东西的时候. 比如: 定时执行, 如果不用异步方法,也不用定时器,只用Thread.Sleep ...

  8. Centos7网络配置,vsftpd安装及530报错解决

    今天在虚拟机安装CentOS7,准备全新安装LTMP,结果又是一堆问题,不过正好因为这些出错,又给自己长了见识. 1,CentOS7网络配置 最小化安装CentOs7后,ifconfig提示comma ...

  9. (喷血分享)利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句

    (喷血分享)利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句 在我们RDIFramework.NET代码生成器中,有这样一个应用,就是通过数据库表自动生成表的CREA ...

  10. Linux:Ubuntu14.04离线安装scala(在线安装)

    参考Scala安装:http://www.lupaworld.com/thread-970271-1-1.html 以下命令安装默认scala版本 sudo apt-get install scala ...