应用场景
当向Executor提交多个任务并且希望获得它们在完成之后的结果,如果用FutureTask,可以循环获取task,并调用get方法去获取task执行结果,但是如果task还未完成,获取结果的线程将阻塞直到task完成,由于不知道哪个task优先执行完毕,使用这种方式效率不会很高。在jdk5时候提出接口CompletionService,它整合了Executor和BlockingQueue的功能,可以更加方便在多个任务执行时获取到任务执行结果。
案例
需求:不使用求和公式,计算从1到1000相加的和。
分析设计:需求指明不能使用求和公式,只能循环依次相加,为了提高效率,我们可以将1到1000的数分为n段由n个task执行,执行结束后merge结果求最后的和。
代码实现:
public class CompletionServiceTest { private static ExecutorService executor = Executors.newFixedThreadPool(100); @Test public void run() { CompletionService<Long> completionService = new ExecutorCompletionService<Long>(executor); final int groupNum = 1000 / 100; for (int i = 1; i <= 100; i++) { final int start = (i - 1) * groupNum + 1; final int end = i * groupNum; completionService.submit(new Callable<Long>() { @Override public Long call() throws Exception { long sum = 0l; for (int j = start; j <= end; j++) { sum += j; } return sum; } }); } long result = 0l; try { for (int i = 0; i < 100; i++) { result += completionService.take().get(); } } catch (Exception e) { e.printStackTrace(); } System.out.println("the result is: " + result); } }
示例二:
监控服务通过发布线程推送信道变化了的数据。
private class PublishTask implements Runnable {
@Override
public void run() {
List<Future<MonitorResult>> futures = new ArrayList<Future<MonitorResult>>();
CompletionService<MonitorResult> completionService = new ExecutorCompletionService<MonitorResult>(executor);
for (final MonitorChannel channel : channelMap.values()) {
futures.add(completionService.submit(new Callable<MonitorResult>() {
@Override
public MonitorResult call() throws Exception {
return queryData(channel);
}
}));
}
for (int i = 0; i < futures.size(); i++) {
try {
MonitorResult result = completionService.take().get();
if (result != null) {
sendEvent(result);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Exception e) {
logger.warn("publish data exception, {}", e.getMessage());
}
}
}
}
CompletionService源码分析
CompletionService接口提供五个方法:
Future<V> submit(Callable<V> task)
提交Callable类型的task;Future<V> submit(Runnable task, V result)
提交Runnable类型的task;Future<V> take() throws InterruptedException
获取并移除已完成状态的task,如果目前不存在这样的task,则等待;Future<V> poll()
获取并移除已完成状态的task,如果目前不存在这样的task,返回null;Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException
获取并移除已完成状态的task,如果在指定等待时间内不存在这样的task,返回null。