关于CompletableFuture的一切,看这篇文章就够了
java中CompletableFuture的使用
之前的文章中,我们讲解了Future, 本文我们将会继续讲解java 8中引入的CompletableFuture的用法。
CompletableFuture首先是一个Future,它拥有Future所有的功能,包括获取异步执行结果,取消正在执行的任务等。
除此之外,CompletableFuture还是一个CompletionStage。
我们看下CompletableFuture的定义:
public class CompletableFuture<T> implements Future<T>, CompletionStage<T>
什么是CompletionStage呢?
在异步程序中,如果将每次的异步执行都看成是一个stage的话,我们通常很难控制异步程序的执行顺序,在javascript中,我们需要在回调中执行回调。这就会形成传说中的回调地狱。
好在在ES6中引入了promise的概念,可以将回调中的回调转写为链式调用,从而大大的提升了程序的可读性和可写性。
同样的在java中,我们使用CompletionStage来实现异步调用的链式操作。
CompletionStage定义了一系列的then*** 操作来实现这一功能。
CompletableFuture作为Future使用
调用CompletableFuture.complete方法可以立马返回结果,我们看下怎么使用这个方法来构建一个基本的Future:
public Future<String> calculateAsync() throws InterruptedException {
CompletableFuture<String> completableFuture
= new CompletableFuture<>();
Executors.newCachedThreadPool().submit(() -> {
Thread.sleep(500);
completableFuture.complete("Hello");
return null;
});
return completableFuture;
}
上面我们通过调动ExecutorService来提交一个任务从而得到一个Future。如果你知道执行的结果,那么可以使用CompletableFuture的completedFuture方法来直接返回一个Future。
public Future<String> useCompletableFuture(){
Future<String> completableFuture =
CompletableFuture.completedFuture("Hello");
return completableFuture;
}
CompletableFuture还提供了一个cancel方法来立马取消任务的执行:
public Future<String> calculateAsyncWithCancellation() throws InterruptedException {
CompletableFuture<String> completableFuture = new CompletableFuture<>();
Executors.newCachedThreadPool().submit(() -> {
Thread.sleep(500);
completableFuture.cancel(false);
return null;
});
return completableFuture;
}
如果这个时候调用Future的get方法,将会报CancellationException异常。
Future<String> future = calculateAsyncWithCancellation();
future.get(); // CancellationException
异步执行code
CompletableFuture提供了runAsync和supplyAsync的方法,可以以异步的方式执行代码。
我们看一个runAsync的基本应用,接收一个Runnable参数:
public void runAsync(){
CompletableFuture<Void> runAsync= CompletableFuture.runAsync(()->{
log.info("runAsync");
});
}
而supplyAsync接受一个Supplier:
public void supplyAsync(){
CompletableFuture<String> supplyAsync=CompletableFuture.supplyAsync(()->{
return "supplyAsync";
});
}
他们两个的区别是一个没有返回值,一个有返回值。
组合Futures
上面讲到CompletableFuture的一个重大作用就是将回调改为链式调用,从而将Futures组合起来。
而链式调用的返回值还是CompletableFuture,我们看一个thenCompose的例子:
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> "Hello")
.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " World"));
thenCompose将前一个Future的返回结果作为后一个操作的输入。
如果我们想合并两个CompletableFuture的结果,则可以使用thenCombine:
public void thenCombine(){
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> "Hello")
.thenCombine(CompletableFuture.supplyAsync(
() -> " World"), (s1, s2) -> s1 + s2));
}
如果你不想返回结果,则可以使用thenAcceptBoth:
public void thenAcceptBoth(){
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> "Hello")
.thenAcceptBoth(CompletableFuture.supplyAsync(() -> " World"),
(s1, s2) -> System.out.println(s1 + s2));
}
thenApply() 和 thenCompose()的区别
thenApply()和thenCompose()两个方法都可以将CompletableFuture连接起来,但是两个有点不一样。
thenApply()接收的是前一个调用返回的结果,然后对该结果进行处理。
thenCompose()接收的是前一个调用的stage,返回flat之后的的CompletableFuture。
简单点比较,两者就像是map和flatMap的区别。
并行执行任务
当我们需要并行执行任务时,通常我们需要等待所有的任务都执行完毕再去处理其他的任务,那么我们可以用到CompletableFuture.allOf方法:
public void allOf(){
CompletableFuture<String> future1
= CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2
= CompletableFuture.supplyAsync(() -> "Beautiful");
CompletableFuture<String> future3
= CompletableFuture.supplyAsync(() -> "World");
CompletableFuture<Void> combinedFuture
= CompletableFuture.allOf(future1, future2, future3);
}
allOf只保证task全都执行,而并没有返回值,如果希望带有返回值,我们可以使用join:
public void join(){
CompletableFuture<String> future1
= CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2
= CompletableFuture.supplyAsync(() -> "Beautiful");
CompletableFuture<String> future3
= CompletableFuture.supplyAsync(() -> "World");
String combined = Stream.of(future1, future2, future3)
.map(CompletableFuture::join)
.collect(Collectors.joining(" "));
}
上面的程序将会返回:“Hello Beautiful World”。
异常处理
如果在链式调用的时候抛出异常,则可以在最后使用handle来接收:
public void handleError(){
String name = null;
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> {
if (name == null) {
throw new RuntimeException("Computation error!");
}
return "Hello, " + name;
}).handle((s, t) -> s != null ? s : "Hello, Stranger!");
}
这和Promise中的catch方法使用类似。
本文的例子可以参考https://github.com/ddean2009/learn-java-concurrency/tree/master/CompletableFuture
更多教程请参考 flydean的博客
相关文章
- 阿里安全宣布已完美越狱iOS 12
- 库克7年之后用“芯”爬坡,双卡双待只是敲门砖
- 支付宝小程序来了!可能是马云宣布卸任后,阿里最重磅的战略!
- 国产手机到底凭什么翻身?
- 8月小程序TOP100榜单暨报告: 互联网企业入局加快,用户群体扩大
- 过去一年里,苹果核心业务都摊上了哪些“幺蛾子”?
- iOS 12 用户遭遇系统「疯狂」弹窗,但目前并没有很好的解决方法
- 有两个以上苹果设备,却不知道这5个功能?你的钱白花了
- 盘点 iOS 工程 12 和安卓 9.0 相互学习了哪些特性
- 做小程序设计,不得不说的7个坑
- 这10条认知,可能就是你和大厂UI 设计师的差距
- 苹果发布iOS 12系统第七个开发者测试版
- iOS开发中runtime常用的几种方法
- 小程序洞察报告:传统品牌几大玩法、几大困惑都在这里了
- 3D成像技术来袭,金融科技将迎来哪些变革?
- 优雅快速的搭建iOS工程框架的探讨
- 拼命鼓吹5G手机的,不是傻就是坏
- 谷歌正式推出Android P第五个开发者预览版更新
- 假如安卓“无法保持免费”,谷歌能做些什么?
- iOS 内存管理:内存优化