写在前面
我第一次接触 CompletableFuture,是想把几个互不依赖的查询并行执行。后来发现它不只是“开线程”,还涉及线程池、异常处理、任务组合和代码可读性。
这篇是基础学习笔记,只记录我现阶段能讲清楚、也能在项目里用到的部分。
基础用法
有返回值的异步任务:
1 2 3
| CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { return "hello"; });
|
无返回值的异步任务:
1 2 3
| CompletableFuture<Void> future = CompletableFuture.runAsync(() -> { System.out.println("run task"); });
|
实际使用时,我更倾向于传入自定义线程池,而不是直接使用默认线程池。
1 2
| CompletableFuture<UserInfo> userFuture = CompletableFuture.supplyAsync(() -> userService.getUser(userId), executor);
|
任务组合
如果几个查询互不依赖,可以用 allOf 等待全部完成:
1 2 3 4 5 6 7 8 9 10
| CompletableFuture<UserInfo> userFuture = CompletableFuture.supplyAsync(() -> userService.getUser(userId), executor);
CompletableFuture<UserConfig> configFuture = CompletableFuture.supplyAsync(() -> configService.getConfig(userId), executor);
CompletableFuture.allOf(userFuture, configFuture).join();
UserInfo user = userFuture.join(); UserConfig config = configFuture.join();
|
这里要注意,allOf 本身不直接返回每个任务的结果,结果还需要单独取。
异常处理
异步任务里的异常如果不处理,很容易让问题变得不明显。
1 2 3 4 5 6
| CompletableFuture<UserInfo> future = CompletableFuture.supplyAsync(() -> userService.getUser(userId), executor) .exceptionally(ex -> { log.warn("query user failed, userId={}", userId, ex); return null; });
|
是否返回默认值,要看业务是否允许。如果是核心数据失败,应该直接返回错误;如果是非核心补充信息,可以考虑降级。
小结
CompletableFuture 适合多个互不依赖任务的组合查询,但不能为了异步而异步。使用时我会重点关注:
- 是否真的需要并行
- 是否使用独立线程池
- 异常有没有处理
- 代码是否还容易维护