📚 Java 21 结构化并发漫画手册
程序猿:并发编程头都大了!线程泄漏、异常乱飞、任务取消难管理,天天写Bug😫
Java精灵:试试结构化并发呀!把所有任务关进「作用域笼子」,生命周期统一管,出错一起扛,资源绝不漏~
场景一:微服务扇出调用 | 多路接口并行查
🎬 小剧场:订单服务要查详情,得同时喊用户、商品、物流三个接口。以前一个挂了其他还在傻跑,现在结构化并发当总管,一个失败全叫停!
🏪 业务场景:电商订单详情聚合、多接口数据聚合、并行查询第三方服务
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Subtask<User> userTask = scope.fork(() -> userApi.getUser(userId));
Subtask<Product> prodTask = scope.fork(() -> productApi.getProduct(prodId));
Subtask<Logistics> logiTask = scope.fork(() -> logisticsApi.getLogistics(orderId));
scope.join().throwIfFailed();
return new OrderDetail(
userTask.get(),
prodTask.get(),
logiTask.get()
);
}
- 单个任务失败时,自动取消所有其他任务,不浪费线程资源
- 异常统一向上传播,无需逐个捕获处理,代码更简洁
- 任务生命周期与作用域绑定,退出自动清理,从根源避免线程泄漏
场景二:批量任务超时管控 | 到点全下班
🎬 小剧场:产品经理说这批任务必须5秒内出结果!以前超时了线程还在后台偷偷跑,现在结构化并发到点直接全员取消,绝不拖泥带水~
📊 业务场景:报表生成、批量数据计算、限时查询任务、超时降级场景
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
List<Subtask<ReportItem>> tasks = dataIds.stream()
.map(id -> scope.fork(() -> calcReportItem(id)))
.toList();
scope.joinUntil(Instant.now().plusSeconds(5));
scope.throwIfFailed();
return tasks.stream()
.map(Subtask::get)
.toList();
}
- 全局超时控制,超时自动终止所有子任务
- 避免超时任务后台占用资源,保护系统稳定性
- 配合降级逻辑,轻松实现超时兜底方案
场景三:分治计算任务 | 大任务拆小齐心算
🎬 小剧场:超大数组求和算不完?拆成N份,每个小任务算一段,最后拼起来!结构化并发管着所有小任务,层级清晰不混乱~
🔢 业务场景:大文件统计、数组并行计算、日志分析、数据分块处理
long parallelSum(long[] arr, int left, int right) {
if (right - left <= 1000) {
return Arrays.stream(arr, left, right).sum();
}
int mid = (left + right) / 2;
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
var leftTask = scope.fork(() -> parallelSum(arr, left, mid));
var rightTask = scope.fork(() -> parallelSum(arr, mid, right));
scope.join().throwIfFailed();
return leftTask.get() + rightTask.get();
}
}
- 天然适配分治思想,任务层级结构与代码结构完全对应
- 子任务生命周期绑定父作用域,出错逐层向上传递
- 比 ForkJoinPool 更直观的结构化管理,避免任务泄漏
场景四:嵌套任务编排 | 任务套娃不乱套
🎬 小剧场:主任务下面有A、B两组,A组又分A1、A2两个小任务!嵌套作用域安排上,每层管自己的娃,各层互不干扰~
🏗️ 业务场景:复杂业务流程编排、商品上架、多级数据处理、有层级依赖的并发任务
try (var mainScope = new StructuredTaskScope.ShutdownOnFailure()) {
var baseTask = mainScope.fork(() -> getBaseInfo(productId));
var assetTask = mainScope.fork(() -> {
try (var assetScope = new StructuredTaskScope.ShutdownOnFailure()) {
var imgTask = assetScope.fork(() -> processImages(productId));
var descTask = assetScope.fork(() -> generateDesc(productId));
assetScope.join().throwIfFailed();
return new AssetResult(imgTask.get(), descTask.get());
}
});
mainScope.join().throwIfFailed();
return buildProduct(baseTask.get(), assetTask.get());
}
- 支持多层级任务嵌套,作用域互相隔离,边界清晰
- 每层独立管理生命周期,错误不跨层扩散
- 代码结构与业务逻辑一一对应,可读性和可维护性更强
场景五:事务型并发操作 | 要成一起成,要败一起败
🎬 小剧场:这几个数据库操作必须同时成功!不然全回滚!结构化并发开启「一败全败」模式,一个挂了全叫停,完美配合事务回滚~
💳 业务场景:多数据源写入、分布式事务、批量支付、原子性并发操作
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
List<Subtask<Boolean>> tasks = orders.stream()
.map(order -> scope.fork(() -> accountService.credit(order)))
.toList();
scope.join().throwIfFailed();
transaction.commit();
} catch (Exception e) {
transaction.rollback();
throw e;
}
- 快速失败机制,一个任务失败立即终止其他所有任务
- 便于统一事务提交/回滚逻辑,保证操作原子性
- 避免部分成功部分失败的数据不一致状态
场景六:多源竞速查询 | 谁快用谁的
🎬 小剧场:查数据同时喊缓存和数据库,谁先回来就用谁的!以前得自己等最快的,现在 ShutdownOnSuccess 模式,第一个成功直接全员下班~
⚡ 业务场景:多级缓存查询、冗余服务调用、接口容灾竞速、最快结果获取
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>()) {
scope.fork(() -> redisCache.get(key));
scope.fork(() -> mysqlDb.query(key));
scope.fork(() -> backupApi.query(key));
return scope.join().result();
}
- 自动获取最快返回的结果,显著提升接口响应速度
- 第一个任务成功后,自动取消其余慢任务,节省资源
- 天然适配多副本容灾、多级缓存降级等场景
Java精灵:结构化并发让并发编程回归「结构化」,生命周期可控、错误处理清晰、资源零泄漏~
💡 小提示:Java 21 中结构化并发处于预览阶段,使用时需添加 JVM 参数 --enable-preview 哦~