1. 引言
在现代 Java 开发中,异步编程是提升应用性能的重要手段。CompletableFuture 作为 Java 8 引入的异步编程工具,不仅提供了丰富的 API 用于任务管理,还能让代码更加优雅地处理并发逻辑。
本文将深入解析 CompletableFuture 的常见方法,结合实际代码示例,并统计任务执行耗时
,帮助你更好地理解 CompletableFuture 在实际开发中的应用场景。
2. CompletableFuture
常见方法及应用场景
2.1 supplyAsync() & runAsync() —— 创建异步任务
- supplyAsync(Supplier):有返回值
- runAsync(Runnable):无返回值
- 适用场景:
远程 API 调用、数据库查询、文件 IO
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(1000); } catch (InterruptedException e) { }
return "Hello, CompletableFuture!";
});
System.out.println(future.get()); // 阻塞等待执行结果
2.2 thenApply() —— 任务结果转换
- 适用场景:对异步任务的结果进行后续计算
Instant start = Instant.now();
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { }
return 10;
}).thenApply(result -> result * 2); //将 result*2
System.out.println("cost: " + Duration.between(start, Instant.now()).toSeconds() + "s , result is " + future.get());
2.3 thenCompose() —— 连接两个异步任务
- **适用场景:**第一个任务的结果作为第二个异步任务的输入
static void testThenCompose() throws ExecutionException, InterruptedException {
Instant start = Instant.now();
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { }
return 10;
}).thenCompose(result -> CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { }
return result * 2;
}));
System.out.println("cost: " + Duration.between(start, Instant.now()).toSeconds() + "s , result is " + future.get());
}
- 执行结果分析:
f1(2s)+f2(3s) ≈ 5s
2.4 thenCombine() —— 合并两个异步任务的结果
- 适用场景:合并数据库查询、合并 API 请求数据
static void testThenCombine() throws ExecutionException, InterruptedException {
Instant start = Instant.now();
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(7); } catch (InterruptedException e) { }
return 10;
});
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(13); } catch (InterruptedException e) { }
return 20;
});
CompletableFuture<Integer> combinedFuture = future1.thenCombine(future2, Integer::sum);
System.out.println("cost: " + Duration.between(start, Instant.now()).toSeconds() + "s , result is " + combinedFuture.get());
}
- 执行结果分析:
- future1 运行 7s,future2 运行 13s
- thenCombine() 需要等待两个任务都完成,
所以总时间 ≈ 13s
2.5 allOf() —— 等待所有任务完成
- 适用场景:多个 API 并行请求,全部完成后执行逻辑
static void testAllOf() throws ExecutionException, InterruptedException {
Instant start = Instant.now();
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { }
return 10;
});
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { }
return 20;
});
CompletableFuture<Integer> f3 = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { }
return 30;
});
CompletableFuture<Void> allFutures = CompletableFuture.allOf(f1, f2, f3);
allFutures.join(); // 等待所有任务完成
int sum = f1.get() + f2.get() + f3.get();
System.out.println("cost: " + Duration.between(start, Instant.now()).toSeconds() + "s , result is " + sum);
}
- 执行结果分析: f1(3s),f2(5s),f3(2s),allOf()
取最大时间 ≈ 5s
2.6 anyOf() —— 等待最快的任务完成
- 适用场景:多个数据源查询,获取最快的结果
static void testAnyOf() throws ExecutionException, InterruptedException {
Instant start = Instant.now();
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { }
return "Task 1";
});
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { }
return "Task 2";
});
CompletableFuture<Object> any = CompletableFuture.anyOf(f1, f2);
System.out.println("cost: " + Duration.between(start, Instant.now()).toSeconds() + "s , result is " + any.get());
}
-执行结果分析: f1(5s),f2(3s),
返回最快的 ≈ 3s
2.7 exceptionally() —— 处理异常
- 适用场景:网络请求失败时提供默认值
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
if (true) throw new RuntimeException("任务失败!");
return 10;
}).exceptionally(ex -> {
System.out.println("发生异常:" + ex.getMessage());
return -1;
});
System.out.println(future.get());
3. 总结
方法 | 作用 | 适用场景 |
---|---|---|
supplyAsync() | 创建异步任务(有返回值) | API 调用、数据库查询 |
thenApply() | 转换任务结果 | 计算返回值 |
thenCompose() | 连接两个异步任务 | 任务依赖执行 |
thenCombine() | 合并两个任务的结果 | 订单计算、价格合并 |
exceptionally() | 处理异常 | 任务失败回退 |
allOf() | 等待所有任务完成 | 并行任务 |
anyOf() | 只要有一个完成即返回 | 竞态任务 |
4.其它
4.1指定线程池
在 CompletableFuture 中,supplyAsync() 方法默认使用ForkJoinPool.commonPool()
线程池执行任务。但如果需要使用自定义线程池
,可以传递 Executor 作为参数,例如 Executors.newFixedThreadPool()
。
示例:使用自定义线程池
import java.util.concurrent.*;
public class CompletableFutureWithCustomThreadPool {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService customThreadPool = Executors.newFixedThreadPool(5);
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println("Running in thread: " + Thread.currentThread().getName());
return 42;
}, customThreadPool);
System.out.println("Result: " + future.get());
customThreadPool.shutdown(); // 记得关闭线程池
}
}
执行结果
Running in thread: pool-1-thread-1 Result: 42
此示例使用了 固定大小的线程池,确保 CompletableFuture 任务在特定的线程池中执行,而不会与默认的 ForkJoinPool 共享资源。
适用场景
• 限制并发线程数,避免 ForkJoinPool 线程数失控
• 需要隔离 CompletableFuture 任务与其他任务(如 Web 服务器主线程)
• 适用于 数据库查询、API 调用、CPU 密集型计算