一、背景介绍
在 Java 后端开发中,我们经常会遇到以下问题:
- 需要并行执行多个数据库查询或远程调用;
- 单线程执行多个
.list()
方法时耗时过长; - 希望提升系统响应速度,但又不想引入过多框架。
这时,Java 8 提供的 CompletableFuture.supplyAsync()
就是非常强大的解决方案,它结合线程池使用,可实现高效、简洁的并发处理。
二、什么是 CompletableFuture.supplyAsync
CompletableFuture
是 Java 8 引入的异步编程类,用于简化多线程编程。
supplyAsync()
是它最常用的方法之一,用于:
在后台线程中执行一个有返回值的任务
✅ 基本语法:
CompletableFuture<ResultType> future = CompletableFuture.supplyAsync(() -> {
// 模拟一个耗时操作,比如查询数据库、调用接口
return result;
});
✅ 指定线程池:
ExecutorService executor = Executors.newFixedThreadPool(4);
CompletableFuture<ResultType> future = CompletableFuture.supplyAsync(() -> {
return result;
}, executor);
三、实战案例:并发加载多个数据字典
💡 场景描述:
系统首页需要加载多个数据字典:
- 单位列表(单位ID → 名称)
- 系统列表(系统ID → 名称)
- 专家信息(ID → 姓名)
- 测评机构(单位ID → 公司名称)
如果顺序调用 .list()
,性能会非常低。可以改用 CompletableFuture.supplyAsync
并行处理。
🔧 代码实现:
ExecutorService executor = Executors.newFixedThreadPool(5);
CompletableFuture<Map<String, String>> unitMapFuture = CompletableFuture.supplyAsync(() -> {
List<UnitInfoDO> units = iUnitInfoService.list();
return units.stream().collect(Collectors.toMap(UnitInfoDO::getUnitId, UnitInfoDO::getUnitName, (k1, k2) -> k1));
}, executor);
CompletableFuture<Map<String, String>> systemMapFuture = CompletableFuture.supplyAsync(() -> {
List<UnitSystemDO> systems = unitSystemService.list();
List<BeforehandDO> befores = beforehandService.list();
Map<String, String> result = systems.stream().collect(Collectors.toMap(
UnitSystemDO::getSystemId, UnitSystemDO::getName, (k1, k2) -> k1
));
Map<String, String> beforeMap = befores.stream().collect(Collectors.toMap(
BeforehandDO::getSystemId, BeforehandDO::getName, (k1, k2) -> k1
));
result.putAll(beforeMap);
return result;
}, executor);
CompletableFuture<Map<String, String>> expertMapFuture = CompletableFuture.supplyAsync(() -> {
return iIndustryExpertInfoService.list().stream().collect(Collectors.toMap(
expert -> String.valueOf(expert.getId()), IndustryExpertInfoDO::getName, (k1, k2) -> k1
));
}, executor);
CompletableFuture<Map<String, String>> companyMapFuture = CompletableFuture.supplyAsync(() -> {
return measurementCompanyInfoService.list().stream().collect(Collectors.toMap(
MeasurementCompanyInfoDO::getUnitId, MeasurementCompanyInfoDO::getCompanyName, (k1, k2) -> k1
));
}, executor);
// 等待全部完成
CompletableFuture.allOf(unitMapFuture, systemMapFuture, expertMapFuture, companyMapFuture).join();
// 获取结果
Map<String, String> unitMap = unitMapFuture.join();
Map<String, String> systemMap = systemMapFuture.join();
Map<String, String> expertMap = expertMapFuture.join();
Map<String, String> companyMap = companyMapFuture.join();
// 关闭线程池
executor.shutdown();
四、常见用法总结
方法 | 用法 | 说明 |
---|---|---|
supplyAsync() | 有返回值的异步执行 | 常用于并发任务 |
thenApply() | 对结果做转换处理 | 类似 .map() |
thenAccept() | 消费结果,无返回值 | 类似 .forEach() |
exceptionally() | 异常处理 | 给出默认值 |
whenComplete() | 最终处理 | 获取结果+异常 |
✅ 异常处理示例:
CompletableFuture<Map<String, String>> safeFuture = CompletableFuture.supplyAsync(() -> {
return dangerousCall();
}).exceptionally(ex -> {
log.error("执行失败", ex);
return Collections.emptyMap(); // 返回默认值
});
五、注意事项
supplyAsync
本质上是异步线程执行,适合耗时任务,不适合小逻辑;join()
会阻塞线程,使用前确保其他任务已经触发;- 强烈建议使用线程池(如
Executors.newFixedThreadPool
)来控制线程数量,防止系统资源耗尽; - 在 Spring 中建议使用
@Async + TaskExecutor
替代手动线程池。