CompletableFuture 是 Java 8 引入的一个非常强大的工具,用于编写异步、非阻塞的代码。它代表了未来某个时刻会完成的计算结果,并提供了丰富的 API 来组合、转换和处理异步任务。
核心场景一:执行耗时任务(避免阻塞主线程)
这是最基础的用途。当你有一个耗时的操作(如IO、网络请求、复杂计算)时,不希望它阻塞当前线程(特别是UI线程或服务器的主请求线程),就可以使用 CompletableFuture 将其提交到另一个线程中异步执行。
示例:异步计算并获取结果
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class BasicExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1. 使用 supplyAsync 执行一个有返回值的异步任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 模拟一个耗时2秒的任务
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return "任务执行结果";
});
// 主线程可以继续做其他事情,不会被阻塞
System.out.println("主线程可以继续执行...");
// 2. 阻塞地等待异步任务完成并获取结果(在实际应用中应尽量避免阻塞)
String result = future.get();
System.out.println("获取到结果: " + result);
}
}
核心场景二:组合多个异步任务(链式调用)
这是 CompletableFuture 最强大的地方。你可以定义一个任务完成之后,紧接着执行另一个任务,形成一条异步流水线。
示例:第一个任务的结果是第二个任务的输入
CompletableFuture<String> welcomeText = CompletableFuture.supplyAsync(() -> {
// 模拟从数据库获取用户ID
try { Thread.sleep(500); } catch (InterruptedException e) { }
return "User123";
}).thenApply(userId -> {
// 根据用户ID获取用户详细信息
return userId + " 的详细信息";
}).thenApply(userInfo -> {
// 组装欢迎信息
return "您好, " + userInfo;
});
System.out.println(welcomeText.get()); // 输出: 您好, User123 的详细信息
关键方法:
thenApply(): 接收上一个任务的结果,进行处理并返回新值(同步操作)。thenCompose(): 接收上一个任务的结果,并返回一个新的 CompletableFuture(用于链接另一个异步任务)。
核心场景三:聚合多个并行任务(全部完成或任一完成)
当你需要并行执行多个独立的任务,并在它们都完成后(或其中一个完成后)进行后续处理时,这个功能非常有用。
示例:并行调用多个外部API,并聚合它们的结果
// 模拟三个不同的API调用
CompletableFuture<String> api1 = CompletableFuture.supplyAsync(() -> callAPI("https://api.users.com"));
CompletableFuture<String> api2 = CompletableFuture.supplyAsync(() -> callAPI("https://api.products.com"));
CompletableFuture<String> api3 = CompletableFuture.supplyAsync(() -> callAPI("https://api.orders.com"));
// 1. 等待所有任务完成
CompletableFuture<Void> allFutures = CompletableFuture.allOf(api1, api2, api3);
// 在所有任务完成后,获取它们的结果
CompletableFuture<List<String>> allResults = allFutures.thenApply(v ->
Stream.of(api1, api2, api3)
.map(CompletableFuture::join) // 在这里调用join是安全的,因为所有future已完成
.collect(Collectors.toList())
);
allResults.thenAccept(results -> System.out.println("所有API结果: " + results));
// 2. 等待任意一个任务完成
CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(api1, api2, api3);
anyFuture.thenAccept(result -> System.out.println("最快返回的API结果: " + result));
关键方法:
allOf(): 等待所有给定的CompletableFuture完成。anyOf(): 等待任意一个给定的CompletableFuture完成。
核心场景四:异常处理
异步流水线中的异常不能被传统的 try-catch 捕获,CompletableFuture 提供了专门的异常处理方法。
示例:优雅地处理异步任务中的异常
CompletableFuture.supplyAsync(() -> {
// 模拟一个可能失败的任务
if (Math.random() > 0.5) {
throw new RuntimeException("Oops! Something went wrong!");
}
return "Success";
}).exceptionally(ex -> {
// 如果发生异常,会进入这里,并提供一个默认值或恢复方案
System.out.println("处理异常: " + ex.getMessage());
return "Default Value"; // 从异常中恢复
}).thenAccept(result -> {
// 无论成功还是失败(已处理),都会走到这里
System.out.println("最终结果: " + result);
});
关键方法:
exceptionally(): 相当于catch,允许你捕获异常并返回一个默认值。handle(): 无论成功还是失败都会调用,方法接收结果和异常两个参数,让你可以统一处理。
核心场景五:结果消费(无需返回值的后续操作)
当你不需要返回新值,只是想在上一个任务完成后执行一些操作(如日志记录、发送通知),可以使用 thenAccept 或 thenRun。
示例:用户注册后发送欢迎邮件
CompletableFuture.supplyAsync(() -> {
// 1. 执行用户注册逻辑
return registerUser();
}).thenAccept(user -> {
// 2. 注册成功后,使用用户信息发送邮件(消费上一步的结果)
sendWelcomeEmail(user);
}).thenRun(() -> {
// 3. 无论成功失败,最后记录日志(不消费任何结果)
System.out.println("用户注册流程结束。");
});
关键方法:
thenAccept(): 接收上一个任务的结果,进行消费(无返回值)。thenRun(): 既不接收上一个任务的结果,也不返回任何值,只是在一个任务完成后执行一段代码。
总结:何时使用 CompletableFuture
| 场景 | 描述 | 常用方法 |
|---|---|---|
| 异步执行 | 将耗时任务提交到线程池,避免阻塞主线程 | supplyAsync, runAsync |
| 任务链 | 一个任务接一个任务地执行,后续任务依赖前一个任务的结果 | thenApply, thenCompose |
| 聚合结果 | 并行执行多个独立任务,并处理它们的聚合结果 | allOf, anyOf, thenCombine |
| 异常处理 | 优雅地处理异步流水线中可能发生的异常 | exceptionally, handle |
| 结果消费 | 在任务完成后执行一些副作用操作,如日志、通知 | thenAccept, thenRun |
它非常适合用于构建高性能、高并发的服务,例如微服务架构中同时调用多个下游服务并聚合结果的场景。它是传统 Future 的强大升级,提供了非阻塞的回调式编程模型。

1717

被折叠的 条评论
为什么被折叠?



