【前言】
之前一篇博客---《采用Callable接口创建线程》介绍了,Callable接口,它优化了Runable接口,而其中Future接口又代表了Callable接口中的call()方法的返回值。
Future是一种多线程设计模式,可以让用户把要执行的方法交给它,同时可以处理其他的业务逻辑,过段时间可从Future那里取出结果,一般和Callable结合(可使用ExecutorService的submit执行Callable,返回Future)使用。
Future模式的虽然可以实现异步执行结果,但
①只能通过轮询IsDone确认完成之后,使用阻塞的get()方法来获取值。(或者调用get(long timeout, TimeUnit unit)防止程序无限制等待结果)
②没有通知机制,无法得知何时结束,而且isDone会耗费CPU资源。
③异步返回的结果与结果之间,无法建立结果之间的联系。(比如:将两个异步结果合并,or只等待最快的任务,or等待所有任务都结束)
故引出我们今天的主题-CompletableFuture类。(推荐阅读第一条链接)
【介绍】
①位于java.util.concurrent包下。
②CompletableFuture类提供了强大的Future的扩展功能,简化异步编程的复杂度,提供了函数式编程的能力,可通过回调的方式处理计算结果。
③ CompletableFuture类实现了CompletionStage和Future接口。
public class CompletableFuture<T> implements Future<T>, CompletionStage<T>
④在没有指定Executor的情况下,异步执行通过ForkJoinPool来实现,使用守护线程去执行任务。
特点:
① 非阻塞;
②可将异步结果交给另一个异步事件处理线程(不着急要结果,先返回一个结果,后台继续执行那些需要长时间执行的方法,真正需要的时候,再去get真正的结果)。(推荐阅读第二条链接)
③对比联系ForkJoin框架,它是将任务分解为独立的子任务,适用于(处理元素独立,数据集要大,每个任务的处理成本要高,才能弥补框架本身消耗的成本),而CompletableFuture类在ForkJoin的基础上做了创新。
【方法】
1、静态工厂方法
runAsync(Runnable runnable):使用ForkJoinPool.commonPool()作为它的线程池执行异步代码。
runAsync(Runnable runnable, Executor executor)使用指定的thread pool执行异步代码。
supplyAsync(Supplier<U> supplier)使用ForkJoinPool.commonPool()作为它的线程池执行异步代码,异步操作有返回值
supplyAsync(Supplier<U> supplier, Executor executor)使用指定的thread pool执行异步代码,异步操作有返回值
2、其他方法
参考:推荐阅读第三条链接
【一个简单的事例】
异步查询多个班级的学生人数
/**
* 传入classId列表,异步查询每个班的招生人数
*
* @param classIds 传入classId列表
* @return 每个班的招生人数
*/
@Override
public CompletableFuture<List<Map<Long, Integer>>> getStudentCountByClassIds(List<Long> classIds) {
final List<CompletableFuture<Map<Long, Integer>>> collect = classIds.parallelStream().map(s -> CompletableFuture.supplyAsync(() -> {
Map<Long, Integer> map = new HashMap<>();
final int count = progressService.queryClassStudentNumByClassId(s);
map.put(s, count);
return map;
})).collect(Collectors.toList());
CompletableFuture<List<Map<Long, Integer>>> sequence = sequence(collect);
log.info("sequence方法 costTime:{}" ,System.currentTimeMillis()-time);
// CompletableFuture<Integer> totalCount = sequence.thenApply(m -> m.stream().map(s -> s.values().stream().findAny().get()).reduce((a, b) -> a + b).get());
return sequence;
}
//endregion
private static <T> CompletableFuture<List<T>> sequence(List<CompletableFuture<T>> futures){
CompletableFuture<Void> completableFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
return completableFuture.thenApply(v->
futures.stream()
.map(future-> future.join())
.collect(Collectors.toList())
);
}
【推荐阅读】
Java8新的异步编程方式 CompletableFuture(一)
Java并发编程系列一:Future和CompletableFuture解析与使用
20 个使用 Java CompletableFuture的例子
推荐一个公众号,免费领架构师学习资料,每周更新优质文章,能学到很多。