并发编程(四)CompletionService
1.1 什么是CompletionService
简单的并行任务我们直接使用线程池各自提交任务就可以了,需要获取结果的或者线程之间有依赖关系的我们可以使用Future,当然对于复杂的关系例如线程之间的串行,并行,聚合等关系,java也提供了ComplableFuture来简化我们对线程的操作。
CompletionService java提供的一种批量处理线程的类CompletionService。
1.2 CompletionService基本原理和使用
其实就是内部维护了一个阻塞队列,阻塞队列里边存储了各个线程执行完之后的Future对象。
CompletionService的实现类有ExecutorCompletionService两个构造方法 :
/**
* 如果不传如阻塞队列默认使用的是一个无界的阻塞队列LinkedBlockingQueue
*/
public ExecutorCompletionService(Executor executor) {
if (executor == null)
throw new NullPointerException();
this.executor = executor;
this.aes = (executor instanceof AbstractExecutorService) ?
(AbstractExecutorService) executor : null;
this.completionQueue = new LinkedBlockingQueue<Future<V>>();
}
public ExecutorCompletionService(Executor executor,
BlockingQueue<Future<V>> completionQueue) {
if (executor == null || completionQueue == null)
throw new NullPointerException();
this.executor = executor;
this.aes = (executor instanceof AbstractExecutorService) ?
(AbstractExecutorService) executor : null;
this.completionQueue = completionQueue;
}
CompletionService执行批量任务
/**
* 任务的批量处理
* @author:
* @Date: 2019-06-26 18:28
* @Copyright: 2019 www.lenovo.com Inc. All rights reserved.
*/
public class CompletionServiceDemo {
public static void main(String[] args)throws Exception {
//两个构造方法 不传队列使用默认的无界队列 LinkedBlockingQueue
CompletionService service1 = new ExecutorCompletionService(TestThreadPool.getInstance());
service1.submit(()->{
TimeUnit.SECONDS.sleep(3);
return "task1";
});
service1.submit(()->{
TimeUnit.SECONDS.sleep(4);
return "task2";
});
service1.submit(()->{
TimeUnit.SECONDS.sleep(1);
return "task3";
});
for(int i=0;i<3;i++){
Future take = service1.take();
System.out.println(take.get());
}
}
}
CompletionService的重要方法
Future<V> submit(Callable<V> task);
Future<V> submit(Runnable task, V result);
/**
*从阻塞队列中获取一个Future 并且删除,如果队列为空阻塞
*/
Future<V> take()
throws InterruptedException;
/**
*从阻塞队列中获取一个Future 并且删除,如果队列为空返回null
*/
Future<V> poll();
/**
*从阻塞队列中获取一个Future 并且删除,支持超时机制,超过超时时间还没有获取到结果,返回null
*/
Future<V> poll(long timeout, TimeUnit unit)
throws InterruptedException;
1.4 实现Dubbo的Forking Cluster
首先要明白Dubbo的ForkingCluster是什么 ,其实就是并行的调用多个查询服务,只要有一个成功返回数据,这个服务就可以返回了。
利用CompletionService可以非常简单的实现这个功能。
/**
* @author:
* @Date: 2019-06-26 19:26
* @Copyright: 2019 www.lenovo.com Inc. All rights reserved.
*/
public class DubboForkingClusterSimulation {
public static void main(String[] args)throws Exception {
ExecutorCompletionService<String> batchService = new ExecutorCompletionService(TestThreadPool.getInstance());
List<Future> tasks = new ArrayList<>(3);
tasks.add(batchService.submit(()->f1()));
tasks.add(batchService.submit(()->f2()));
tasks.add(batchService.submit(()->f3()));
for(int i=0;i<3;i++){
String result = batchService.take().get();
System.out.println(result);
if(! StringUtils.isEmpty(result)){
break;
}
}
tasks.forEach(f->{
f.cancel(true);
});
}
private static String f1()throws Exception{
TimeUnit.SECONDS.sleep(6);
return "f1 invoke success";
}
private static String f2()throws Exception{
TimeUnit.SECONDS.sleep(3);
return "f2 invoke success";
}
private static String f3()throws Exception{
TimeUnit.SECONDS.sleep(8);
return "f3 invoke success";
}
}
上边的代码中有一个服务成功返回之后,就停止其他的服务,进行返回。