Java批处理ExecutorService/CompletionService
服务端接收一个请求,常常需要同时进行几个计算或者向其他服务发送请求,最后拼装结果返回上游。本文就来看下JDK提供几个并行处理方案,牵涉到ExcecutorService/CompletionService。要实现的场景是请求有超时限制,如果所有操作都计算完成,则全部拼装返回;否则只拼装部分完成的结果。
1.前提
//任务类,sleep一个时间代表这个计算需要的耗时,返回一个计算结果。
public class MyTask implements Callable<Integer> {
private int id;
private int time;
public MyTask(int i, int time) {
this.id = i;
this.time = time;
}
@Override
public Integer call() throws Exception {
Thread.sleep(time);
return id;
}
}
//线程池
ExecutorService threadPool = new ThreadPoolExecutor(10, 20,
60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
2.任务逐个等待
提交任务后,逐个等待结果返回。
//总共150ms超时
List<Future<Integer>> futures = new ArrayList<Future<Integer>>();
futures.add(threadPool.submit(new MyTask(1, 60)));
futures.add(threadPool.submit(new MyTask(2, 150)));
Integer res = 0; for (int i = 0; i < futures.size(); ++i) {
try {
Integer tmp = futures.get(i).get(50 + i * 50, TimeUnit.MILLESECONDS);
res += tmp;
} catch (Exception e) {
//nothing
}
}
System.out.println(res);
打印结果为0,实际上第60ms时第一个计算已经完成,本可以返回第一个结果的。这是因为生产者和消费者一一对应,调度不当。
3.等待解耦
用一个队列来实现生产者和消费者解耦,生产者把结果放到一个队列中,消费者从队列中取结果,不按照任务提交顺序等待,只要有结果就会消耗。CompletionService就是对Executor和异步队列的封装。
CompletionService<Integer> service = new ExecutorCompletionService<Integer>(threadPool);
List<Future<Integer>> futures = new ArrayList<Future<Integer>>();
futures.add(service.submit(new MyTask(1, 60)));
futures.add(service.submit(new MyTask(2, 150))); List<Integer> result = new ArrayList<Integer>(futures.size());
for (int i = 0; i < futures.size(); ++i) {
Future<Integer> future = service.poll(50 + i * 50, TimeUnit.MILLISECONDS);
if (null != future){
result.add(future.get());
}
}
int res = 0;
for (Integer i : result) {
res += null == i ? 0 : i;
}
System.out.println(res);
打印结果为1,即第一个计算的结果。谁先完成就取谁。
4.充分利用时间
前面两个方案为每个任务都设置了一个固定的等待时间,两者之和不超过超时限制。这没有充分利用时间,两个任务是并发互不影响的,其各自可利用的时间应该是超时时间,而不应该是两者之和为超时时间。如果第一个任务耗时140,第二个耗时60,前两个任务就只能获得第二个任务的结果。如果两者都设置超时为150,就能获得2个结果。
List<MyTask> tasks = new ArrayList<MyTask>();
tasks.add(new MyTask(1, 140));
tasks.add(new MyTask(2, 60));
List<Future<Integer>> futures = threadPool.invokeAll(tasks, 150, TimeUnit.MILLISECONDS); int res = 0;
for (Future<Integer> future : futures) {
System.out.println("isDone:" + future.isDone());
System.out.println("isCancel:" + future.isCancelled());
if (future.isCancelled()) {
continue;
}
res += future.get();
}
System.out.println(res);
打印结果是3。上述方法利用ExecutorService的invokeAll方法,该方法在所有任务都完成或等待超时时返回,超时的时候会取消还没有完成的任务,通过判断任务是否被取消来判断任务是否计算完成。
该文章说的是结果互不影响、有结果就能返回的应用场景。如果结果需要按照先后顺序进行合并,或者只需要等待一个计算完成,就不一定需要方案3了。
Java批处理ExecutorService/CompletionService的更多相关文章
- Java线程之CompletionService批处理任务
如果你向Executor提交了一个批处理任务,并且希望在它们完成后获得结果,怎么办呢? 为此你可以保存与每个任务相关联的Future,然后不断地调用 timeout为零的get,来检验Future是否 ...
- 【Java多线程】CompletionService
什么是CompletionService? 当我们使用ExecutorService启动多个Callable时,每个Callable返回一个Future,而当我们执行Future的get方法获取结果时 ...
- java批处理、MySQL批处理
e: cd MySQL\bin mysql -uroot -proot @pause MySQL批处理.bat e: cd JAVA\jdk1.8.0_77\bin javac Hello.java ...
- Java中ExecutorService和CompletionService区别
我们现在在Java中使用多线程通常不会直接用Thread对象了,而是会用到java.util.concurrent包下的ExecutorService类来初始化一个线程池供我们使用. 之前我一直习惯自 ...
- java并发编程 Executor,Executors,ExecutorService,CompletionService,Future,C
使用CompletionService获取多线程返回值 CompletionService和ExecutorCompletionService详解 Java并发编程系列之十五:Executor框架
- Java线程之CompletionService
转自:http://blog.csdn.net/andycpp/article/details/8902699 当使用ExecutorService启动了多个Callable后,每个Callable会 ...
- java中ExecutorService接口
一.声明 public interface ExecutorService extends Executor 位于java.util.concurrent包下 所有超级接口:Executor 所有已知 ...
- Java批处理操作
批量,可以大大提高众多增加.删除.变化的步伐,它是有一个非常大的数据处理效率大收益. 的"连接池"相似.事实上就是先将多次操作(增删改)打包.然后再一次发送运行 主要用到两个方法: ...
- Java 1.ExecutorService四种线程池的例子与说明
1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? new Thread(new Runnable() { @Override public void run() { ...
随机推荐
- Web前端工程师成长之路——知识汇总
一.何为Web前端工程师? 前端工程师,也叫Web前端开发工程师.他是随着web发展,细分出来的行业.Web前端开发工程师,主要职责是利用(X)HTML/CSS/JavaScript ...
- LeetCode 412. Fizz Buzz
Problem: Write a program that outputs the string representation of numbers from 1 to n. But for mult ...
- update maven之后jre被改成1.5的问题
在 pom.xml 中添加如下代码: <build> <plugins> <plugin> <groupId>org.apache.maven.plug ...
- GRU(Gated Recurrent Unit) 更新过程推导及简单代码实现
GRU(Gated Recurrent Unit) 更新过程推导及简单代码实现 RNN GRU matlab codes RNN网络考虑到了具有时间数列的样本数据,但是RNN仍存在着一些问题,比如随着 ...
- Linux内核笔记——内存管理之slab分配器
内核版本:linux-2.6.11 内存区和内存对象 伙伴系统是linux用于满足对不同大小块内存分配和释放请求的解决方案,它为slab分配器提供页框分配请求的实现. 如果我们需要请求具有连续物理地址 ...
- ECF R9(632E) & FFT
Description: 上一篇blog. Solution: 同样我们可以用fft来做...就像上次写的那道3-idoit一样,对a做k次卷积就好了. 同样有许多需要注意的地方:我们只是判断可行性, ...
- 斯考特·杨(Scott Young)快速学习方法
上午在网上看到了斯考特·杨(Scott Young)的快速学习方法,感觉很受鼓舞. 现在已经读研究生了,可是发现自己自从上大学以来到现在,发现自己的学习方法有很大的问题. 我是个特别喜欢读书的人,在大 ...
- 使用canal分析binlog(二) canal源码分析
在能够跑通example后有几个疑问 1. canal的server端对于已经读取的binlog,client已经ack的position,是否持久化,保存在哪里 2. 即使不启动zookeeper, ...
- JSON相关知识,转载:删除JSON中数组删除操作
一:JSON是什么 JSONg格式:对象是一个无序的“名称/值”对的集合. 对象以括号开始,括号结束. 名称冒号分隔值. "名称/值"之间用逗号分隔 例: var people = ...
- HOW TO RUN A SPRINT PLANNING MEETING (THE WAY I LIKE IT)
This is a sample agenda for a sprint planning meeting. Depending on your context you will have to ch ...