CompletableFuture 详解
CompletableFuture
是 Java 8 引入的一个类,位于 java.util.concurrent
包中,它支持异步编程,并且提供了比传统的 Future
更强大的功能。CompletableFuture
既可以用于异步任务的执行,也可以用于实现组合多个异步任务、处理异步结果以及异常处理等复杂操作。
CompletableFuture
实现了 Future
接口,同时它还提供了丰富的 API 来执行异步计算、注册回调函数、进行多线程组合等。
1. CompletableFuture 基本概念
CompletableFuture
是一个可组合的异步任务。当一个任务异步执行完毕时,它会将结果提供给注册的回调函数。通过 CompletableFuture
,可以组合多个异步任务,轻松处理并发任务之间的依赖关系。
2. 创建 CompletableFuture
2.1 使用 supplyAsync
启动异步任务
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
return 100;
});
supplyAsync
会异步执行传入的Supplier
任务,并返回一个CompletableFuture
对象。
2.2 使用 runAsync
启动无返回值的异步任务
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
// 执行异步任务,不需要返回值
System.out.println("Task executed");
});
runAsync
用于执行一个无返回值的异步任务。
2.3 创建已完成的 CompletableFuture
CompletableFuture<Integer> completedFuture = CompletableFuture.completedFuture(42);
completedFuture
创建一个已完成的CompletableFuture
,并返回指定的结果。
3. 异步任务的组合
3.1 thenApply
:转换返回值
thenApply
用于对任务的返回值进行转换,它会在原任务完成后执行,并返回一个新的结果。
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 100)
.thenApply(result -> result * 2); // 将返回值乘以 2
thenApply
返回一个新的CompletableFuture
,该CompletableFuture
会在前一个任务完成后执行,并对返回的结果进行处理。
3.2 thenAccept
:消费返回值
thenAccept
用于对结果进行消费,但不返回结果。
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> "Hello")
.thenAccept(result -> System.out.println("Processed: " + result));
thenAccept
是一种无返回值的操作,它仅对结果进行消费操作。
3.3 thenRun
:后续任务
thenRun
用于执行一个没有输入参数的后续操作,后续操作不会使用前一个任务的结果。
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> 100)
.thenRun(() -> System.out.println("Task completed"));
thenRun
可以在前一个任务完成后执行某个操作,但没有输入参数。
3.4 thenCombine
:合并两个异步任务的结果
thenCombine
可以将两个独立的异步任务的结果合并起来进行后续处理。
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 20);
CompletableFuture<Integer> result = future1.thenCombine(future2, (res1, res2) -> res1 + res2);
thenCombine
返回一个新的CompletableFuture
,它会等到两个原任务完成,然后合并它们的结果。
3.5 thenCompose
:扁平化异步任务
thenCompose
用于将多个异步任务串联起来,后续任务依赖前一个任务的结果,返回一个新的 CompletableFuture
。
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 100)
.thenCompose(result -> CompletableFuture.supplyAsync(() -> result * 2));
thenCompose
适用于处理返回类型为CompletableFuture
的异步任务,从而避免了多层嵌套。
3.6 allOf
:等待所有任务完成
allOf
用于等待多个 CompletableFuture
任务同时完成,并返回一个新的 CompletableFuture<Void>
。
CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(future1, future2);
allOf
适用于多个任务并行执行的场景,等待所有任务完成后再执行后续操作。
3.7 anyOf
:等待任意一个任务完成
anyOf
用于等待多个 CompletableFuture
中的任意一个任务完成,并返回一个新的 CompletableFuture
,它会以第一个完成的任务的结果为结果。
CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(future1, future2);
anyOf
适用于多个任务中只需要一个成功的场景。
4. 异常处理
CompletableFuture
提供了多个方法来处理异步任务中的异常。
4.1 exceptionally
:异常处理
exceptionally
用于处理 CompletableFuture
执行过程中出现的异常,并返回一个默认值。
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("Exception");
}).exceptionally(ex -> {
System.out.println("Exception occurred: " + ex.getMessage());
return 0; // 返回默认值
});
exceptionally
可以捕获并处理异常,返回一个默认的值来继续执行后续操作。
4.2 handle
:处理结果和异常
handle
用于同时处理结果和异常,可以根据任务是否成功执行来决定返回什么结果。
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 100)
.handle((result, ex) -> {
if (ex != null) {
System.out.println("Error: " + ex.getMessage());
return -1;
}
return result * 2;
});
handle
方法会在任务完成后执行,无论任务是正常完成还是异常结束。
4.3 whenComplete
:在任务完成后处理
whenComplete
会在任务完成后执行,无论是正常完成还是出现异常。它与 handle
相似,但是 whenComplete
不允许修改返回值。
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 100)
.whenComplete((result, ex) -> {
if (ex != null) {
System.out.println("Error occurred: " + ex.getMessage());
} else {
System.out.println("Completed with result: " + result);
}
});
whenComplete
仅用于任务的后处理,不改变结果。
5. 阻塞方法
尽管 CompletableFuture
提供了异步编程的强大支持,但也提供了阻塞方法,允许你获取最终结果。
5.1 get()
Integer result = future.get(); // 阻塞直到任务完成,获取结果
get()
会阻塞当前线程,直到CompletableFuture
完成并返回结果。
5.2 join()
Integer result = future.join(); // 阻塞直到任务完成,获取结果
join()
与get()
类似,但是join()
会捕获并包装异常为CompletionException
,而get()
会抛出原始的ExecutionException
。
5.3 getNow()
Integer result = future.getNow(0); // 如果任务已经完成,返回结果;否则返回指定的默认值
getNow()
会返回当前结果,或者如果任务尚未完成,则返回一个默认值。
6. 使用场景
- 异步计算:执行需要时间的任务,如文件下载、网络请求、数据库操作等。
- 异步任务组合:当多个任务之间存在依赖关系或需要组合时,可以通过
thenApply
、thenCombine
等方法将它们组合起来。 - 并行执行:多个独立的异步任务可以并行执行,通过
allOf
或anyOf
等方法等待多个任务的完成。 - 异常处理:可以在异步任务中方便地处理异常,通过
exceptionally
、handle
等方法保证任务的健壮性。
总结
CompletableFuture
是 Java 提供的用于异步编程的工具,它提供了比传统 Future
更强大的功能,支持任务组合、异常处理和多线程同步等功能。