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() { ...
随机推荐
- redhat6下安装Lighttpd1.4.43
学完了C语言,自信满满地冲着开源软件去了,首选了Lighttpd,这个软件代码量不多,适合初入开源的朋友 redhat下安装Lighttpd,一定要先安装依赖库,pcre和bzip2,这两个自行下载, ...
- 使用Packet Sniffer抓包和分析(z-stack协议)
以下内容仅是自己学习总结,可能会有错误,有发现问题的欢迎指正(图片可以自己放大,还是比较清晰的). 1.协调器上电,其他设备均不上电,抓包如下: 通过观察可以发现,协调器建立网络成功后,会以15秒为周 ...
- npm-async使用
async.series(tasks, callback) tasks可以是对象或数组,返回类型就是参数类型 tasks中传入回调函数的第一个值非空即停止后面函数执行 按照顺序流程进行 async.s ...
- 返水bug-霸世
NOOK(N) CSBFB(25) off(Y) QQ(2652880032) G(1) off1(Y) QQ1(3479301404) G1(1) off2(Y) QQ2(309235846) G2 ...
- 在 case 语句中使用字符串-转
http://www.cnblogs.com/del/archive/2008/07/08/1237856.html 非常遗憾 Delphi 的 case 语句不支持字符串, 但我觉得这也可能是基于效 ...
- wpf 触发器理解
(1)属性触发器:其对应的类是Trigger.它在特定关联属性发生变化时被触发.一个属性的更改会在另一个属性中触发即时或动态更改. (2)数据触发器:其对应的类是DataTrigger.它在特定的CL ...
- Unity5中WebGL平台封装的一些技巧
最近在接触unity的WebGL平台,其实这个平台作为Web Player的替代品,已经能满足大部分的开发需求,而且不需要额外的插件支持,确实方便了不少,但开发中依旧遇到了不少问题,在这里记录和共享一 ...
- Linux下添加apache虚拟主机
一切在确保apache被正确安装的前提下 设置虚拟主机 创建虚拟目录 现在,让我们继续安装虚拟主机.虚拟主机命名为local.gis. 创建一个公用的文件夹来存放这虚拟主机的数据. 首先,让我们为lo ...
- 【BZOJ】3997: [TJOI2015]组合数学
题意 \(N \times M\)的网格,一开始在\((1, 1)\)每次可以向下和向右走,每经过一个有数字的点最多能将数字减1,最终走到\((N, M)\).问至少要走多少次才能将数字全部变为\(0 ...
- servlet中文乱码问题
通过response对象向页面输出内容时遇到的乱码问题可分为两种情况 1.字节流 字节流输出时可以通过设置响应头"Content-Type"的值为"text/html;c ...