我们已经在之前的文章中了解使用了 Future 接口,但是 Future 方法有它的缺陷, Future 的 get () 方法是一个阻塞调用,它需要等待任务计算完成,然后检索结果进行后需操作,在这之前程序的所有操作都会被阻塞。
Future 接口的缺点:
无法完成 Future 时我们只能尝试取消任务
Future 接口中的 get() 方法是一个阻塞操作
Future 不支持异常处理
多个 Future 异步任务不能连接传递
Java 8 中引入 CompletableFuture ,是 Future 接口的一种实现,可以传入回调对象,属于 java.util.concurrent package。当异步任务完成或者发生异常时,CompletableFuture 自动调用回调对象的回调方法。下面的代码演示了简单 CompletableFuture 对象的创建。
CompletableFuture completableFuture = new CompletableFuture<>();
assertTrue(completableFuture.isDone());
assertEquals("message", completableFuture.getNow(null));复制代码
getNow (null) 返回已完成的结果 ,否则返回 null 。
isDone() 可以用来获取是否已经完成执行。
public Future calculateAsync() throws InterruptedException {
CompletableFuture completableFuture = new CompletableFuture<>();
Executors.newCachedThreadPool().submit(() -> {
Thread.sleep(500);
completableFuture.complete("Hello");
return null;
});
return completableFuture;
}复制代码
让我们了解 CompletableFuture 相关的各种 API 方法,学习如何利用它们来构建异步操作或数据管道。再此之前先了解一下 CompletionStage 接口。
CompletionStage 接口
CompletionStage 接口可能表示计算流水线中的异步操作。您可以想象一堆任务链接在一起,每个任务的计算依赖于前一个任务/异步操作的结果。
CompletionStage 接口有几个方法,可用于链接多个计算或任务以实现最终结果,例如,thenAccept ()、 thenApply ()、 thenCombine ()、 thenRun ()、 whenComplete ()。实现 CompletionStage 接口可以提供实现这些效果的方法 。
CompletableFuture 的常用方法
CompletableFuture 异步执行结果再次处理,返回的都是CompletionStage 阶段
CompletableFuture.runAsync(Runnable runnable) 执行没有返回值任务。
CompletableFuture.supplyAsync(Supplier supplier) 执行有返回值任务
supplyAsync():该方法传入一个用于计算返回 CompletableFuture 对象的函数。
CompletableFuture cfQuery = CompletableFuture.supplyAsync(() -> {
return queryCode("查找数据库");
});复制代码
completedFuture(U value):返回一个完成的 CompletableFuture,它已经用给定的值计算完成。
CompletableFuture cf1 = CompletableFuture.completedFuture("这是一个 String 字符串")复制代码
complete(T value) : 传入给定的值,调用后如果 CompletableFuture 任务完成转换到 completed 状态,返回 true。
boolean result = completableFuture.complete("Future result value");复制代码
任务流程控制
thenApply () : 执行函数并应用于前一阶段任务执行后的返回值。它返回一个保存函数结果的 CompletionStage。
CompletableFuture f1 = //...
CompletableFuture f2 = f1.thenApply(Integer::parseInt);复制代码
thenAccept () : 调用一个函数并返回 CompletionStage 。thenAccept () 通常在任务管道的最后阶段调用,并返回最终值。
CompletableFuture f1 = //...
CompletableFuture f2 =f1.theAccept(Integer::parseInt);复制代码
thenRun () : 返回一个新的 CompletionStage,可以传入在完成返回的 CompletionStage 之前要执行的动作
CompletableFuture f1 = //...
CompletableFuture f2 = f1.thenRun(() -> System.out.println("Computation finished."));复制代码
thenCombine () : 返回一个新的 CompletionStage,第一个阶段与联合的第二个阶段完成的结果传入联合的第二个阶段中传入的计算函数进行计算。
CompletableFuture f1 = CompletableFuture.supplyAsync(() -> "Hello")
.thenCombine(
CompletableFuture.supplyAsync(() -> " associates! Its thenCombined")
, (value1, value2) -> value1 + value2
);
completableFuture.thenAccept(System.out::println); // Hello associates! Its thenCombined复制代码
thenAcceptBoth () : 返回一个新的CompletionStage,当这个阶段和另一个给定阶段都正常完成时,将以这两个结果作为提供的操作的参数来执行。
String original = "message";
StringBuilder result = new StringBuilder();
CompletableFuture.completedFuture(original)
.thenApply(String::toUpperCase)
.thenAcceptBoth(CompletableFuture.completedFuture(original)
.thenApply(String::toLowerCase),(s1, s2) -> result.append(s1 + s2));
复制代码
runAfterBoth () : 返回一个新的 CompletionStage,当这个阶段和另一个给定阶段都正常完成时,执行给定的动作。
String original = "message";
StringBuilder result = new StringBuilder();
CompletableFuture.completedFuture(original)
.thenApply(String::toUpperCase)
.runAfterBoth(
CompletableFuture.completedFuture(original)
.thenApply(String::toLowerCase)
,() -> result.append("done")
);
thenApplyAsync 传入一个计算函数用于计算返回的 CompletionStage 值。thenApplyAsync 会返回一个新 CompletionStage
// 第一个任务:
CompletableFuture cfQuery = CompletableFuture.supplyAsync(() -> {
return queryCode("中国石油");
});
// cfQuery成功后继续执行下一个任务:
CompletableFuture cfFetch = cfQuery.thenApplyAsync((code) -> {
return fetchPrice(code);
});复制代码
异常处理
completeExceptionally(): 如果这个 CompletableFuture 以出现异常,则返回 true。可能的原因包括取消、完全异常的显式调用和 CompletionStage 操作的突然终止。
CompletableFuture f1 = CompletableFuture.completedFuture("message");
f1.completeExceptionally(new RuntimeException("completed exceptionally"));复制代码
exceptionally(): 完成时返回一个新的 CompletableFuture,CompletableFuture 计算出现异常时会执行传入的函数参数
// 如果执行异常
CompletableFuture.exceptionally((e) -> {
e.printStackTrace();
return null;
});复制代码
CompletableFuture 的其他场景
CompletableFuture 构建步骤管道,每个步骤都可以异步执行,每个步骤取决于前面一个或多个步骤的结果。在 《使用CompletableFuture》 这篇文章中详细讲解了 anyOf() 这种控制方式,感兴趣可以进一步查看。
参考资料: