Java中ExecutorService和CompletionService区别
我们现在在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区别的更多相关文章
- ExecutorService和CompletionService区别
ExecutorService和CompletionService区别: ExecutorService:一直习惯自己维护一个list保存submit的callable task所返回的Future对 ...
- java中ArrayList 、LinkList区别
转自:http://blog.csdn.net/wuchuanpingstone/article/details/6678653 个人建议:以下这篇文章,是从例子说明的方式,解释ArrayList.L ...
- java 中 ==和equals 的区别
Java中equals和==的区别 java中的数据类型,可分为两类: 1.基本数据类型,也称原始数据类型.byte,short,char,int,long,float,double,boolea ...
- java中equals和==的区别 (转)
java中equals和==的区别 值类型是存储在内存中的堆栈(以后简称栈),而引用类型的变量在栈中仅仅是存储引用类型变量的地址,而其本身则存储在堆中. ==操作比较的是两个变量的值是否相等,对于引 ...
- 【转】Java中equals和==的区别
[转]Java中equals和==的区别 java中的数据类型,可分为两类: 1.基本数据类型,也称原始数据类型.byte,short,char,int,long,float,double,boole ...
- java中a++与++a区别
java中a++与++a区别 a++与++a的区别,如果单独使用没有任何区别,如果在运算中就有区别了,a++是先运算在赋值,而++a是先赋值在运算!! 先看a++的代码哦 class demo1 { ...
- c#与java中byte字节的区别及转换方法
原文:c#与java中byte字节的区别及转换方法 在java中 byte的范围在 [-128,127] 在C#中 byte的范围在 [0,255] 所以 java程序与C#程序 进行数据传输的时 ...
- java中==与equel的区别
值类型是存储在内存中的堆栈(以后简称栈),而引用类型的变量在栈中仅仅是存储引用类型变量的地址,而其本身则存储在堆中. ==操作比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量在堆中存储的地 ...
- Java中的“&”和“&&”的区别
Java中的"&"和"&&"的区别 1."&"是位运算符,"&&"是逻辑 ...
随机推荐
- Enum的使用
在项目开发中经常会使用到枚举,下面将举个例子,展示枚举的使用,不说废话,直接上代码. package com.tom.enumTest; public enum StatusType { A(&quo ...
- 如何使用 vimdiff 来 git diff /svn diff
#git 如何实现vimdiffgit config --global diff.tool vimdiff git config --global difftool.prompt false git ...
- 【java基础学习】字符串
字符串 1. java内存区域(堆区.栈区.常量池) 2. String方法 获取长度 length(); 获取位置 indexOf(index); lastIndexOf(index) 获取子串 c ...
- AFN 2.6 code报错总结
1. 错误打印 code=-1016 filed: text/html 错误原因:AFN默认不能解析请求回来的text/html数据 解决办法: AFN3.0的请看这里 AFHTTPSessionM ...
- 8 ways rich people view the world differently than the average person
Self-made millionaire Steve Siebold spent 26 years interviewing some of the wealthiest people in the ...
- c#:浅克隆和深克隆,序列化和反序列化
一.浅克隆和深克隆(浅复制和深复制)浅克隆和深克隆最典型的应用是数据集对象DataSet的Clone和Copy方法.Clone()方法用来复制DataSet的结构,但是不复制DataSet的数据,实现 ...
- LeetCode Graph Valid Tree
原题链接在这里:https://leetcode.com/problems/graph-valid-tree/ 题目: Given n nodes labeled from 0 to n - 1 an ...
- Jmeter工作原理
- 关于Left join
Sql关联表时条件放在On或者where上结果是不一样的. 1.放在on上 如下 select S.StoreID,* from BizProductItem as P left join BizSt ...
- git如何使用 svn如何使用
git和svn是2款常用的版本控制系统. git 的功能: 1.从服务器上克隆完整的Git仓库(包括代码和版本信息)到单机上. 也就是说自己机器上有一个git仓库. 这和svn是不同的,svn是没有本 ...