CompletableFuture
CompletableFuture
CompletableFuture 是 Java 8 引入的一个强大的异步编程工具,它实现了 Future 和 CompletionStage 接口,提供了丰富的 API 来处理异步计算和组合多个异步操作。
如果不指定线程池,默认使用 ForkJoinPool,大量耗时短的操作可以充分利用 CPU 多核能力(分治、工作窃取)
主要特点
- 异步执行:可以在后台线程中执行任务
- 链式调用:可以串联多个异步操作
- 组合操作:可以组合多个 CompletableFuture
- 异常处理:提供了完善的异常处理机制
- 手动完成:可以手动设置完成状态和结果
常用方法
1. 创建 CompletableFuture
supplyAsync()传入的是 Supplier,runAsync()传入的是 Runnable,都是异步操作- 默认使用
ForkJoinPool.commonPool()线程池,也可以指定自定义的线程池
// 提交一个任务(代码块作为任务)
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// TODO
return "Hello";
});
// 提交一个任务(线程就是任务)
CompletableFuture<String> future = CompletableFuture.runAsync(() -> {
// TODO
});
// 使用自定义线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
CompletableFuture<String> futureWithExecutor = CompletableFuture.supplyAsync(() -> {
// TODO
return "Result";
}, executor);
2. 处理单个CompletableFuture结果
两类方法,区别是本次处理是否有返回值
thenApply 系列
| 方法 | 同步/异步 | 线程行为 | 返回值 |
|---|---|---|---|
thenApply(Function) |
同步 | 沿用上一任务的线程 | CompletableFuture<U> |
thenApplyAsync(Function) |
异步 | 默认 ForkJoinPool.commonPool() |
CompletableFuture<U> |
thenApplyAsync(Function, Executor) |
异步 | 使用指定线程池 | CompletableFuture<U> |
thenAccept 系列
| 方法 | 同步/异步 | 线程行为 | 返回值 |
|---|---|---|---|
thenAccept(Consumer) |
同步 | 沿用上一任务的线程 | CompletableFuture<Void> |
thenAcceptAsync(Consumer) |
异步 | 默认 ForkJoinPool.commonPool() |
CompletableFuture<Void> |
thenAcceptAsync(Consumer, Executor) |
异步 | 使用指定线程池 | CompletableFuture<Void> |
示例
// 有返回值
CompletableFuture<String> greetingFuture = future.thenApply(name -> {
return name + " World!";
});
// 无返回值(返回值是null)
future.thenAccept(result -> {
System.out.println("Received: " + result);
});
3. 处理多个 CompletableFuture
-
调度线程阻塞,直到多个 CompletableFuture 执行完成(信号量和计数器也能实现)
// 创建三个异步任务 CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> { sleep(1000); return "任务1完成"; }); CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> { sleep(1500); return "任务2完成"; }); CompletableFuture<String> task3 = CompletableFuture.supplyAsync(() -> { sleep(800); return "任务3完成"; }); // 使用allOf等待所有任务完成 CompletableFuture<Void> allFutures = CompletableFuture.allOf(task1, task2, task3); // 阻塞直到所有任务完成 allFutures.get(); System.out.println("所有任务已完成:"); System.out.println(task1.get()); System.out.println(task2.get()); System.out.println(task3.get()); -
两个 CompletableFuture 执行完成再执行,和上面的区别是可以使用 BiFunction 直接处理结果
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello"); CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World"); // 合并两个独立的结果 CompletableFuture<String> combined = future1.thenCombine(future2, (s1, s2) -> s1 + " " + s2); // 结果将是 "Hello World" combined.thenAccept(System.out::println); -
多个 CompletableFuture 比赛,当有一个完成时,取消其他
类似 thenCombine 和 allOf,也有多任务和两个任务的版本
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 1); CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 2); CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> 3); // 多个任务看谁跑得快 CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future1, future2, future3); anyOf.thenAccept(result -> System.out.println("Any result: " + result)); // 两个任务看谁跑得快 CompletableFuture<Integer> resultFuture = future1.applyToEither(future2, result -> result * 10);
4. 异常处理
exceptionally:任务出现异常执行
CompletableFuture.supplyAsync(() -> {
if (true) throw new RuntimeException("Error");
return "Success";
}).exceptionally(ex -> {
System.err.println("Exception: " + ex.getMessage());
return "Fallback";
});
whenComplete:不管任务是否出现异常都会执行,当异常时具有 exceptionally 的功能
CompletableFuture.supplyAsync(() -> {
if (true) throw new RuntimeException("Something went wrong");
return "Success";
}).whenComplete((result, ex) -> { // 任务完成或异常走这里
if (ex != null) { // 当没有异常
System.err.println("Failed with exception: " + ex.getMessage());
} else { // 当发生异常
System.out.println("Completed successfully with result: " + result);
}
});
handle:如果同步任务(同一个线程串行任务时)如果发生异常,会影响整个链的执行
// 不使用 handle(打印结果会是null,因为异常了)
public static void main(String[] args){
CompletableFuture.supplyAsync(() -> 5) // 任务 1( 返回5 )
.thenApply((v) -> v + 10) // 任务 2(返回 5+10 )
.thenApply((v) -> v / 0) // 任务 3 (返回 15/0,报错 )
.thenApply((v) -> v + 10) // 任务 4
.whenComplete((r, e) -> {
System.out.println(r);
});
}
// 使用 handle(打印结果是10)
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> 5) // 任务 1
.thenApply((v) -> v / 0) // 任务 2(异常)
.thenApply((v) -> v + 10) // 任务 3
.handle((r, e) -> { // 前面的任务(任务1、任务2、任务3任意一个)是否有异常
if (e != null) {
return 0;
} else {
return r;
}
})
.thenApply((v) -> v + 10) // 任务 4
.whenComplete((r, e) -> {
System.out.println(r);
});
}
5. 手动完成
只是是 new 出来的 CompletableFuture才需要设置状态,不过一般不会这样用
CompletableFuture<String> manualFuture = new CompletableFuture<>();
// 手动完成
manualFuture.complete("Manual Result");
// 手动异常完成
manualFuture.completeExceptionally(new RuntimeException("Failed"));
注意事项
-
线程池选择:
- 默认使用 ForkJoinPool.commonPool()
- 对于I/O密集型任务,建议使用自定义线程池
- 避免在大量任务中使用默认线程池,可能导致资源耗尽
-
回调执行线程:
- then* 方法默认在完成当前阶段的同一线程执行
- 使用 *Async 方法可以在不同线程执行
-
异常传播:
- 异常会沿着链式调用传播,直到被处理
- 如果不处理异常,可能导致静默失败
-
内存泄漏:
- 长时间运行的 CompletableFuture 链可能持有对象引用,导致内存泄漏
- 对于不需要的结果应及时清理
-
超时控制:
- Java 9+ 提供了 orTimeout 和 completeOnTimeout 方法
- Java 8 需要自行实现超时逻辑
-
避免阻塞:
- 尽量避免在异步任务中调用 get() 阻塞方法
- 优先使用回调风格的编程

浙公网安备 33010602011771号