CompletableFuture在JDK1.8之后引入的一个用来处理异步编程的类,是属于java.util.concurrent 包中Future接口的扩展类,主要是用来进行异步计算、非阻塞操作、任务组合操作等。简化了异步任务操作的复杂性,提供了基于流式调用的API,方便了开发者可以通过更加直观的方式来编写阻塞式的代码实现。
基本概念
CompletableFuture是一个表示未来某个时间点会完成的计算结果的对象。它不同于Future接口的只能阻塞等待结果的返回,在CompletableFuture中提供了很多在任务完成后进行的操作方法。通过链式调用这些方法将多个异步任务组合在一起,从而支持多步骤的异步编程。
常用功能
创建CompletableFuture
可以通过
CompletableFuture.completedFuture(value) 方法创建一个立即完成的 CompletableFuture。如下所示,直接创建完成状态的CompletableFuture。
CompletableFuture<String> future = CompletableFuture.completedFuture("Hello");
除了上面的方法还可以通过异步的方式来创建,如下所示是两种异步创建的方法。
- CompletableFuture.runAsync():执行一个没有返回值的异步任务。
- CompletableFuture.supplyAsync():执行一个有返回值的异步任务。
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
// 执行一些任务
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
// 返回任务结果
return "Task Result";
});
链式调用和回调
CompletableFuture提供了通过链式调用的方式来处理异步操作的方式,这样的话可以避免了嵌套回调带来的代码复杂性,如下所示。
thenApply方法会在计算结果之上继续调用一个函数然后返回一个新的CompletableFuture,如下所示。
future.thenApply(result -> result + " World");
thenAccept方法在计算结果上应用一个Consumer,但不返回新的CompletableFuture。
future.thenAccept(result -> System.out.println(result));
thenRun在计算完成后运行一个任务,但不会使用计算结果,如下所示。
future.thenRun(() -> System.out.println("Task completed."));
组合多个CompletableFuture
thenCompose方法,可以将两个异步任务串行化,第二个任务依赖于第一个任务的结果。常用于任务之间存在依赖关系的情况,如下所示。
CompletableFuture<String> combinedFuture = future.thenCompose(result ->
CompletableFuture.supplyAsync(() -> result + " Combined"));
thenCombine方法,是主要将两个独立的CompletableFuture的结果进行合并,如下所示。
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
CompletableFuture<String> combined = future1.thenCombine(future2, (result1, result2) -> result1 + " " + result2);
allOf和anyOf
- allOf:等待多个 CompletableFuture 全部完成。
- anyOf:只要其中任意一个 CompletableFuture 完成即可。
CompletableFuture<Void> all = CompletableFuture.allOf(future1, future2);
CompletableFuture<Object> any = CompletableFuture.anyOf(future1, future2);
异常处理
CompletableFuture 提供了多种异常处理方法,来捕获并处理异步任务中的异常,其中比较常见的方式有如下几种。
- exceptionally:在出现异常时执行补救措施。
future.exceptionally(ex -> {
System.out.println("Exception: " + ex.getMessage());
return "Fallback result";
});
- handle:无论是否出现异常,都会执行该回调。可以对结果或异常进行处理。
future.handle((result, ex) -> {
if (ex != null) {
System.out.println("Exception: " + ex.getMessage());
return "Fallback result";
}
return result;
});
主要优势
CompletableFuture提供了异步计算的方式,通过链式调用,使代码更简洁、易读但是多个依赖关系复杂的CompletableFuture链式操作时,可能会发生死锁,需要谨慎设计依赖关系。
CompletableFuture内部建立了很多的异常处理机制使得异常处理变得更加简单。同时可以轻松地将多个任务串联、并行处理,极大提高了处理复杂任务的能力,但是需要注意异步操作较多时要小心内存和线程资源的使用,避免过度使用异步操作。
示例
展示如何通过CompletableFuture实现任务组合和异常处理,如下所示。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
if (Math.random() > 0.5) throw new RuntimeException("Oops!");
return "Hello";
}).thenApply(result -> result + " World")
.exceptionally(ex -> "Recovered from error")
.thenCombine(CompletableFuture.supplyAsync(() -> " CompletableFuture"), (result1, result2) -> result1 + result2);
future.thenAccept(System.out::println);
总结
总结而言,CompletableFuture 是 Java 异步编程中强大且灵活的工具,适用于各种复杂任务的组合与管理,同时它简化了传统回调的层次结构和异常处理。