文章目录
- 一、并发编程概述
-
- [1.1 传统并发编程的痛点](#1.1 传统并发编程的痛点)
- [1.2 结构化并发的核心理念](#1.2 结构化并发的核心理念)
- 二、结构化并发编程详解
-
- [2.1 核心概念与设计哲学](#2.1 核心概念与设计哲学)
- [2.2 核心API](#2.2 核心API)
-
- [2.2.1 StructuredTaskScope 类](#2.2.1 StructuredTaskScope 类)
- [2.2.2 Task Scope](#2.2.2 Task Scope)
- [2.2.3 共享数据机制](#2.2.3 共享数据机制)
- [2.2.3 线程管理](#2.2.3 线程管理)
- [2.3 结构化并发的内存模型与线程安全](#2.3 结构化并发的内存模型与线程安全)
- [2.4 基础使用流程](#2.4 基础使用流程)
- 三、结构化并发编程使用示例
-
- [3.1 基本用法](#3.1 基本用法)
- [3.2 错误传播与取消](#3.2 错误传播与取消)
- [3.3 嵌套作用域](#3.3 嵌套作用域)
- [3.4 处理订单](#3.4 处理订单)
- 四、结构化并发的两种预定义策略
-
- [4.1 ShutdownOnFailure](#4.1 ShutdownOnFailure)
- [4.2 ShutdownOnSuccess](#4.2 ShutdownOnSuccess)
- 五、结构化并发的实际应用场景
-
- [5.1 电商订单处理](#5.1 电商订单处理)
- [5.2 并行数据聚合(ShutdownOnFailure)](#5.2 并行数据聚合(ShutdownOnFailure))
- [5.3 最快响应优先(ShutdownOnSuccess)](#5.3 最快响应优先(ShutdownOnSuccess))
- [5.4 带超时的并发任务](#5.4 带超时的并发任务)
- [5.5 使用 ScopedValue 共享上下文](#5.5 使用 ScopedValue 共享上下文)
- [5.6 嵌套结构化并发](#5.6 嵌套结构化并发)
- [5.7 用户信息聚合服务](#5.7 用户信息聚合服务)
- [5.8 多镜像文件下载](#5.8 多镜像文件下载)
- [5.9 批量数据处理管道](#5.9 批量数据处理管道)
- 六、结构化并发的生命周期管理
- 八、最佳实践总结
-
- [8.1 任务范围总结](#8.1 任务范围总结)
- [8.2 线程选择策略](#8.2 线程选择策略)
- [8.3 异常处理最佳实践](#8.3 异常处理最佳实践)
- [8.4 性能优化建议](#8.4 性能优化建议)
- [8.5 代码组织原则](#8.5 代码组织原则)
- [8.6 与传统并发方式的对比](#8.6 与传统并发方式的对比)
一、并发编程概述
1.1 传统并发编程的痛点
在 Java 21 之前,开发者通常使用以下方式处理并发任务:
- 直接创建线程 (
Thread
类) - 线程池管理 (
ExecutorService
) - 异步结果处理 (
Future
)
传统并发模型的问题
- 生命周期管理复杂
- 线程的启动、阻塞、终止需要手动控制。
- 容易出现线程泄漏(未正确关闭线程)。
- 异常处理困难
- 异常可能在子线程中抛出,主线程无法直接捕获。
- 多个任务的异常需要逐个检查,逻辑复杂。
- 资源泄漏风险高
- 如果未正确关闭线程池或释放资源,可能导致内存泄漏。
- 代码可读性差
- 并发逻辑分散在代码中,难以追踪任务依赖关系。
1.2 结构化并发的核心理念
Java 21 引入 结构化并发(Structured Concurrency),通过以下核心设计解决上述问题:
- 任务与子任务显式关联
- 子任务的生命周期由父任务控制,父任务结束时子任务自动终止。
- 错误传播自动化
- 子任务的异常会自动传播到父任务,无需手动检查。
- 资源管理简化
- 使用
try-with-resources
自动释放资源(如线程、锁)。
- 使用
- 代码结构清晰
- 并发逻辑通过嵌套代码块体现,任务层次结构一目了然。
传统并发问题 任务泄漏: 线程未正确关闭 取消困难: 父任务取消后子任务继续运行 异常处理复杂: 多个任务异常难以协调 结果聚合繁琐: 多任务结果收集效率低 结构化并发: 任务与代码块生命周期绑定 结构化并发: 取消自动传播 结构化并发: 集中式异常处理 结构化并发: 简化结果聚合
结构化并发的核心原则:并发任务的生命周期应该被限制在明确定义的代码块中,形成可预测的层次结构。
二、结构化并发编程详解
2.1 核心概念与设计哲学
结构化并发是一种编程范式,它将并发任务的执行生命周期限制在明确的语法结构中。在 Java 21 中,这通过 java.util.concurrent
包中的新 API 实现,目前仍处于预览阶段。
当前最新版本为Java26, 本机测试环境为Java24 GA版本.

结构化并发编程处理于第四个预览版本.
JEP 499: Structured Concurrency (Fourth Preview)
java
https://openjdk.org/jeps/499

设计原则:
- 生命周期绑定:子任务的生命周期不能超过其父任务的作用域
- 错误传播:子任务中的异常能够可靠地传播到父任务
- 取消支持:父任务的取消会自动传播到所有子任务
- 可观测性:线程转储和调试信息能清晰反映任务间的层次关系
父任务/主线程 创建 StructuredTaskScope 子任务 1 子任务 2 子任务 3 等待所有子任务完成 处理结果
2.2 核心API
2.2.1 StructuredTaskScope 类
这是结构化并发的核心类,管理一组子任务的执行和生命周期。
java
@PreviewFeature(feature = PreviewFeature.Feature.STRUCTURED_CONCURRENCY)
public class StructuredTaskScope<T> implements AutoCloseable {
private final ThreadFactory factory;
private final ThreadFlock flock;
private final ReentrantLock shutdownLock = new ReentrantLock();
// ...
}
Java 24 的结构化并发核心是 java.util.concurrent.StructuredTaskScope
类,其主要功能包括:
- 创建和管理子任务
- 协调多个并发操作
- 处理任务结果和异常
- 确保资源正确释放
核心类和方法
类/方法 | 说明 |
---|---|
StructuredTaskScope<T> |
管理一组并发任务的作用域 |
fork(Callable<T>) |
在作用域内启动一个子任务 |
join() |
阻塞当前线程,等待所有子任务完成 |
throwIfFailed() |
如果任何任务失败,抛出异常 |
resultNow() |
获取任务结果(需确保任务已成功完成) |
2.2.2 Task Scope
- StructuredTaskScope: 基础任务范围类,管理一组相关任务
ShutdownOnSuccess<T>
:任一任务成功完成后关闭其他任务ShutdownOnFailure
:任一任务失败后关闭其他任务
2.2.3 共享数据机制
- ScopedValue:在任务范围内安全共享不可变数据,替代 ThreadLocal
2.2.3 线程管理
- 与虚拟线程(Virtual Threads)无缝集成,推荐配合使用
2.3 结构化并发的内存模型与线程安全
内存模型设计
- 状态变更 :通过
AtomicReference
保证原子性(如OPEN
→SHUTDOWN
)。 - 任务列表 :使用
ConcurrentLinkedQueue
实现无锁并发。
java
class StructuredTaskScope<T> {
private final AtomicReference<State> state; // 状态管理
private final ConcurrentLinkedQueue<Future<T>> futures; // 任务队列
private final ThreadFactory factory; // 线程工厂
enum State { OPEN, CLOSING, SHUTDOWN }
public Future<U> fork(Callable<? extends U> task) {
ensureOwnerAndState(); // 内存屏障
Future<U> future = new FutureTask<>(task);
futures.add(future);
factory.newThread(future).start();
return future;
}
}
2.4 基础使用流程
使用结构化并发的标准流程可归纳为四步:
- 创建任务范围:使用 try-with-resources 确保自动关闭
- 提交子任务:通过 fork () 方法提交任务并获取 Future
- 等待任务完成:调用 join () 等待所有任务完成或终止
- 处理结果 / 异常:根据任务范围类型处理结果或异常
java
// 结构化并发标准模板
try (var scope = new StructuredTaskScope.XXX()) { // 1. 创建任务范围
// 2. 提交子任务
Future<T1> f1 = scope.fork(task1);
Future<T2> f2 = scope.fork(task2);
scope.join(); // 3. 等待任务完成
// 4. 处理结果/异常
scope.throwIfFailed(); // 或获取result
T1 r1 = f1.resultNow();
T2 r2 = f2.resultNow();
}
三、结构化并发编程使用示例
3.1 基本用法
java
package cn.tcmeta.usestructuredtaskscope;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.TimeUnit;
/**
* @author: laoren
* @description: TODO
* @version: 1.0.0
*/
public class BasicStructuredTaskScopeDemo01 {
public static void main(String[] args) {
// ShutdownOnFailure: 创建一个 StructuredTaskScope.ShutdownOnFailure 对象,用于管理任务
// 任一任务失败后关闭其他任务
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// fork 方法创建一个任务,并返回一个 Future 对象
var task1 = scope.fork(() -> {
try {
TimeUnit.MILLISECONDS.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
return "task 1 result!";
});
// fork 方法创建一个任务,并返回一个 Future 对象
var task2 = scope.fork(() -> {
try {
TimeUnit.MILLISECONDS.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
return "task 2 result!";
});
// 等待所有任务完成
scope.join();
// 检查是否有任务失败
scope.throwIfFailed();
// 获取任务结果
System.out.printf("线程名称: 【%s】 , msg: %s \n", Thread.currentThread().getName(), "result1 = " + task1.get());
System.out.printf("线程名称: 【%s】 , msg: %s \n", Thread.currentThread().getName(), "result2 = " + task2.get());
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
}

关键点:
- 使用
try-with-resources
自动释放资源(关闭StructuredTaskScope
)。 ShutdownOnFailure
策略:任一任务失败时,其他任务会被取消。join()
和throwIfFailed()
配合使用,确保所有任务成功完成后再继续执行。
3.2 错误传播与取消
java
package cn.tcmeta.usestructuredtaskscope;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.TimeUnit;
/**
* @author: laoren
* @description: 错误传播与取消操作
* @version: 1.0.0
*/
public class ErrorHandlingDemo {
static void main() {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
StructuredTaskScope.Subtask<String> task1 = scope.fork(() -> {
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "task 1 result!";
});
var task2 = scope.fork(() -> {
try {
TimeUnit.MILLISECONDS.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
throw new RuntimeException("task 2 error!");
});
scope.join();
// 抛出 ExecutionException,包含 task2 的异常
scope.throwIfFailed();
System.out.printf("线程名称: 【%s】 , msg: %s \n", Thread.currentThread().getName(), " result1 = " + task1.get());
System.out.printf("线程名称: 【%s】 , msg: %s \n", Thread.currentThread().getName(), " result2 = " + task2.get());
} catch (InterruptedException | ExecutionException e) {
System.out.printf("线程名称: 【%s】 , msg: %s \n", Thread.currentThread().getName(), " error occurred: " + e.getCause().getMessage());
}
}
}

关键点:
throwIfFailed()
会抛出第一个失败任务的异常(包装在ExecutionException
中)。ShutdownOnFailure
会取消所有未完成的任务(如 task1)。
java
public void throwIfFailed() throws ExecutionException {
throwIfFailed(ExecutionException::new);
}

java
public class ExecutionException extends Exception {}
3.3 嵌套作用域
java
package cn.tcmeta.usestructuredtaskscope;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.TimeUnit;
/**
* @author: laoren
* @description: 嵌套作用域测试
* @version: 1.0.0
*/
public class NestedScopeDemo {
static void main() {
try (var outerScope = new StructuredTaskScope.ShutdownOnFailure()) {
var outerTask = outerScope.fork(() -> {
try (var innerScope = new StructuredTaskScope.ShutdownOnFailure()) {
var innerTask1 = innerScope.fork(() -> {
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "inner task 1";
});
innerScope.join();
innerScope.throwIfFailed();
return "outer task 1";
}
});
outerScope.join();
outerScope.throwIfFailed();
System.out.printf("线程名称: 【%s】 , msg: %s \n", Thread.currentThread().getName(), " outer result = " + outerTask.get());
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
}
}
关键点:
- 嵌套作用域(
innerScope
)的生命周期由外层作用域(outerScope
)管理。 - 如果
innerScope
中的任务失败,outerScope
会自动取消所有子任务。
3.4 处理订单
java
package cn.tcmeta.usestructuredtaskscope;
// 编译和运行需添加 --enable-preview 参数
import java.util.concurrent.*;
public class StructuredConcurrencyDemo {
// 模拟一个可能失败的服务调用
private static String mockServiceCall(String serviceName, int delay, boolean shouldFail)
throws InterruptedException {
Thread.sleep(delay);
if (shouldFail) {
throw new RuntimeException(serviceName + " failed intentionally");
}
return serviceName + " result after " + delay + "ms";
}
public static void main(String[] args) {
try {
String result = fetchUserDataParallel();
System.out.println("Final result: " + result);
} catch (Exception e) {
System.out.println("Operation failed: " + e.getMessage());
}
}
public static String fetchUserDataParallel() throws ExecutionException, InterruptedException {
// 使用 try-with-resources 确保作用域正确关闭
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// 并行启动三个子任务
StructuredTaskScope.Subtask<String> userInfoTask = scope.fork(() ->
mockServiceCall("UserService", 100, false));
StructuredTaskScope.Subtask<String> orderHistoryTask = scope.fork(() ->
mockServiceCall("OrderService", 200, false));
StructuredTaskScope.Subtask<String> recommendationsTask = scope.fork(() ->
mockServiceCall("RecommendationService", 150, true)); // 这个会失败
// 等待所有任务完成或任何一个失败
scope.join();
// 如果任何任务失败,抛出异常
scope.throwIfFailed();
// 所有任务都成功,组合结果
return String.format("User: %s, Orders: %s, Recomm: %s",
userInfoTask.get(),
orderHistoryTask.get(),
recommendationsTask.get());
} // 作用域结束,自动确保所有子任务完成或取消
}
}

四、结构化并发的两种预定义策略
4.1 ShutdownOnFailure
- 行为:任一子任务失败时,取消所有未完成的任务。
- 适用场景:需要保证原子性的操作(如数据库事务)
java
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
var task1 = scope.fork(() -> {
Thread.sleep(1000);
return "Task 1";
});
var task2 = scope.fork(() -> {
Thread.sleep(500);
throw new RuntimeException("Task 2 Failed");
});
scope.join();
scope.throwIfFailed();
}
4.2 ShutdownOnSuccess
- 行为:任一子任务成功完成时,取消所有未完成的任务。
- 适用场景:需要找出最快完成任务的场景(如缓存预加载)。
java
try (var scope = new StructuredTaskScope.ShutdownOnSuccess()) {
var task1 = scope.fork(() -> {
Thread.sleep(1000);
return "Task 1";
});
var task2 = scope.fork(() -> {
Thread.sleep(500);
return "Task 2";
});
scope.join();
scope.throwIfFailed();
System.out.println("Fastest result: " + scope.results().get(0).get());
}
java
Fastest result: Task 2
五、结构化并发的实际应用场景
5.1 电商订单处理
java
package cn.tcmeta.usestructuredtaskscope;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope;
/**
* @author: laoren
* @description: 订单流程处理
* @version: 1.0.0
*/
public class OrderProcessing {
static void main() {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
var inventoryCheck = scope.fork(() -> {
System.out.println("Checking inventory...");
Thread.sleep(1000);
return true;
});
var paymentVerification = scope.fork(() -> {
System.out.println("Verifying payment...");
Thread.sleep(1500);
return true;
});
var logRecording = scope.fork(() -> {
System.out.println("Recording order log...");
Thread.sleep(500);
return true;
});
scope.join();
scope.throwIfFailed();
if(inventoryCheck.get() && paymentVerification.get() && logRecording.get()){
System.out.println("Order processed successfully.");
}
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
}
}

5.2 并行数据聚合(ShutdownOnFailure)
适用于需要多个并行任务都成功完成才能得到最终结果的场景,如聚合多个服务的数据。
java
package cn.tcmeta.usestructuredtaskscope;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.Future;
import java.time.LocalDateTime;
/**
* 并行获取用户信息、订单列表和推荐商品,聚合后返回
*/
public class DataAggregationDemo {
static void main(String[] args) {
try {
UserDashboard dashboard = fetchUserDashboard("1756866565");
System.out.println("用户仪表盘数据:\n" + dashboard);
} catch (Exception e) {
System.err.println("获取仪表盘数据失败: " + e.getMessage());
}
}
record UserDashboard(User user, OrderList orders, Recommendations recommendations) {}
record User(String id, String name, String email) {}
record OrderList(String userId, int count, LocalDateTime lastOrderTime) {}
record Recommendations(String[] items) {}
public static UserDashboard fetchUserDashboard(String userId) throws Exception {
// 创建任务范围:任一任务失败则关闭其他任务
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// 并行提交三个任务
StructuredTaskScope.Subtask<User> userFuture = scope.fork(() -> fetchUser(userId));
StructuredTaskScope.Subtask<OrderList> ordersFuture = scope.fork(() -> fetchOrders(userId));
StructuredTaskScope.Subtask<Recommendations> recommendationsFuture = scope.fork(() -> fetchRecommendations(userId));
// 等待所有任务完成或任一任务失败
scope.join();
// 检查是否有任务失败,有则抛出异常
scope.throwIfFailed();
// 所有任务成功,聚合结果
return new UserDashboard(
userFuture.get(),
ordersFuture.get(),
recommendationsFuture.get()
);
}
}
// 模拟获取用户信息
private static User fetchUser(String userId) throws InterruptedException {
Thread.sleep(300); // 模拟网络延迟
return new User(userId, "老任", "1756866565@qq.com");
}
// 模拟获取订单列表
private static OrderList fetchOrders(String userId) throws InterruptedException {
Thread.sleep(500); // 模拟网络延迟
return new OrderList(userId, 5, LocalDateTime.now().minusDays(2));
}
// 模拟获取推荐商品
private static Recommendations fetchRecommendations(String userId) throws InterruptedException {
Thread.sleep(400); // 模拟网络延迟
return new Recommendations(new String[]{"商品A", "商品B", "商品C"});
}
}
java
用户仪表盘数据:
UserDashboard[user=User[id=tcmeta, name=老任, email=1756866565@qq.com], orders=OrderList[userId=tcmeta, count=5, lastOrderTime=2025-08-20T13:21:53.907171700], recommendations=Recommendations[items=[Ljava.lang.String;@4769b07b]]
流程解析:
- 三个任务并行执行,总耗时约等于最慢任务的时间(~500ms)
- 任一任务失败会立即取消其他任务,避免资源浪费
- 结果在所有任务成功后统一聚合,保证数据一致性
5.3 最快响应优先(ShutdownOnSuccess)
适用于从多个数据源获取相同数据,只需最快响应结果的场景,如多 CDN 节点内容获取。
java
package cn.tcmeta.usestructuredtaskscope;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.ThreadFactory;
import java.net.URI;
/**
* 从多个镜像服务器获取配置,使用第一个成功返回的结果
*/
public class FastestResponseDemo {
static void main(String[] args) {
String[] mirrorUrls = {
"https://mirror1.example.com/config",
"https://mirror2.example.com/config",
"https://mirror3.example.com/config"
};
try {
String config = fetchConfigFromFastestMirror(mirrorUrls);
System.out.println("使用最快镜像的配置:\n" + config);
} catch (Exception e) {
System.err.println("所有镜像获取配置失败: " + e.getMessage());
}
}
public static String fetchConfigFromFastestMirror(String[] urls) throws Exception {
// 创建虚拟线程工厂,提高效率
ThreadFactory vtf = Thread.ofVirtual().name("mirror-fetcher-", 0).factory();
// 创建任务范围:任一任务成功则关闭其他任务
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>(null, vtf)) {
// 向所有镜像提交获取任务
for (String url : urls) {
String u = url; // 有效final变量
scope.fork(() -> fetchFromMirror(u));
}
// 等待任一任务成功或所有任务失败
scope.join();
// 返回第一个成功的结果
return scope.result();
}
}
// 从单个镜像获取配置
private static String fetchFromMirror(String url) throws Exception {
// 模拟不同镜像的响应延迟(100-1000ms)
long delay = (long) (Math.random() * 900 + 100);
Thread.sleep(delay);
// 模拟偶然失败(10%概率)
if (Math.random() < 0.1) {
throw new RuntimeException("镜像 " + url + " 暂时不可用");
}
System.out.printf("线程名称: 【%s】 , msg: %s \n", Thread.currentThread().getName(), "镜像 " + url + " 响应成功,耗时 " + delay + "ms");
return "配置内容(来自 " + url + ")";
}
}

流程解析:
- 同时向多个镜像发送请求,谁先返回就用谁的结果
- 第一个成功返回后,自动取消其他所有请求
- 配合虚拟线程,高效处理多个网络请求
5.4 带超时的并发任务
结合超时机制,确保并发任务不会无限期等待,适用于对响应时间有严格要求的场景。
java
package cn.tcmeta.usestructuredtaskscope;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* 带超时的并发任务处理,确保在指定时间内完成
*/
public class TimeoutDemo {
static void main(String[] args) {
try {
String result = processWithTimeout();
System.out.println("任务处理结果: " + result);
} catch (TimeoutException e) {
System.err.println("任务超时: " + e.getMessage());
} catch (Exception e) {
System.err.println("任务失败: " + e.getMessage());
}
}
public static String processWithTimeout() throws Exception {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// 提交主任务
var mainTask = scope.fork(() -> {
Thread.sleep(800); // 模拟处理时间
return "主任务结果";
});
// 提交监控任务(超时检查)
var timeoutTask = scope.fork(() -> {
Thread.sleep(500); // 超时阈值
throw new TimeoutException("任务处理超时(>500ms)");
});
// 等待任一任务完成(主任务完成或超时)
scope.join();
// 检查是否有失败
scope.throwIfFailed();
// 返回主任务结果
return mainTask.get();
}
}
}

流程解析:
- 同时运行主任务和超时监控任务
- 若主任务在超时前完成,超时任务会被取消
- 若超时时间到主任务仍未完成,超时任务抛出异常终止整个范围
5.5 使用 ScopedValue 共享上下文
在结构化并发中安全共享上下文信息(如用户认证信息、追踪 ID 等),替代 ThreadLocal。
java
package cn.tcmeta.usestructuredtaskscope;
import javax.security.auth.callback.Callback;
import java.lang.ScopedValue;
import java.util.concurrent.Callable;
import java.util.concurrent.StructuredTaskScope;
/**
* 使用ScopedValue在结构化并发任务间共享上下文
*/
public class ScopedValueDemo {
// 定义可在范围内共享的值
private static final ScopedValue<String> USER_ID = ScopedValue.newInstance();
private static final ScopedValue<String> TRACE_ID = ScopedValue.newInstance();
public static void main(String[] args) {
// 设置共享上下文并执行任务
String userId = "user-123";
String traceId = "trace-" + System.currentTimeMillis();
// 绑定多个ScopedValue
ScopedValue.where(USER_ID, userId)
.where(TRACE_ID, traceId)
.run(() -> {
try {
processUserRequest();
} catch (Exception e) {
System.err.println("处理请求失败: " + e.getMessage());
}
});
}
public static void processUserRequest() throws Exception {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// 提交需要访问上下文的任务
StructuredTaskScope.Subtask<Callable<String>> task1
= scope.fork(ScopedValueDemo::logAccess);
StructuredTaskScope.Subtask<Callable<String>> task2 =scope.fork(ScopedValueDemo::updateUserStatus);
StructuredTaskScope.Subtask<Callable<String>> task3 = scope.fork(ScopedValueDemo::recordActivity);
scope.join();
scope.throwIfFailed();
System.out.println("所有任务完成,上下文共享成功");
}
}
private static Callable<String> logAccess() {
// 访问共享的上下文值
System.out.println("[" + TRACE_ID.get() + "] 记录访问 - 用户: " + USER_ID.get());
return () -> "logAccess!";
}
private static Callable<String> updateUserStatus() {
System.out.println("[" + TRACE_ID.get() + "] 更新状态 - 用户: " + USER_ID.get());
return () -> "updateUserStatus!";
}
private static Callable<String> recordActivity() {
System.out.println("[" + TRACE_ID.get() + "] 记录活动 - 用户: " + USER_ID.get());
return () -> "updateUserStatus!";
}
}
ScopedValue 优势:
- 不可变性:确保共享数据不会被意外修改
- 范围绑定:超出范围自动失效,无内存泄漏风险
- 继承性:子任务自动继承父任务的 ScopedValue
- 性能:比 ThreadLocal 更高效,尤其在虚拟线程环境
5.6 嵌套结构化并发
创建任务的层次结构,实现复杂的并发工作流,如先并行获取数据,再并行处理数据
java
package cn.tcmeta.usestructuredtaskscope;
import java.util.concurrent.Future;
import java.util.concurrent.StructuredTaskScope;
/**
* @author: laoren
* @description: 嵌套结构化并发
* @version: 1.0.0
*/
public class NestedStructuredConcurrencyDemo {
public static void main(String[] args) {
try {
// 顶层任务:处理订单
OrderProcessingResult result = processOrder("ORDER-789");
System.out.println("订单处理结果: " + result);
} catch (Exception e) {
System.err.println("订单处理失败: " + e.getMessage());
}
}
record OrderProcessingResult(PaymentResult payment, InventoryResult inventory, ShippingLabel label) {}
record PaymentResult(boolean success, String transactionId) {}
record InventoryResult(boolean available, int quantity) {}
record ShippingLabel(String labelId, String address) {}
// 顶层任务:处理订单
public static OrderProcessingResult processOrder(String orderId) throws Exception {
try (var orderScope = new StructuredTaskScope.ShutdownOnFailure()) {
// 并行执行三大步骤
StructuredTaskScope.Subtask<PaymentResult> paymentFuture = orderScope.fork(() -> processPayment(orderId));
StructuredTaskScope.Subtask<InventoryResult> inventoryFuture = orderScope.fork(() -> checkInventory(orderId));
StructuredTaskScope.Subtask<ShippingLabel> shippingFuture = orderScope.fork(() -> createShippingLabel(orderId));
orderScope.join();
orderScope.throwIfFailed();
return new OrderProcessingResult(
paymentFuture.get(),
inventoryFuture.get(),
shippingFuture.get()
);
}
}
// 子任务1:处理支付(包含嵌套的结构化并发)
private static PaymentResult processPayment(String orderId) throws Exception {
// 嵌套的任务范围
try (var paymentScope = new StructuredTaskScope.ShutdownOnFailure()) {
// 并行验证支付方式和处理支付
StructuredTaskScope.Subtask<Boolean> validationFuture = paymentScope.fork(() -> validatePaymentMethod(orderId));
StructuredTaskScope.Subtask<String> transactionFuture = paymentScope.fork(() -> processTransaction(orderId));
paymentScope.join();
paymentScope.throwIfFailed();
return new PaymentResult(validationFuture.get(), transactionFuture.get());
}
}
// 子任务2:检查库存
private static InventoryResult checkInventory(String orderId) throws InterruptedException {
Thread.sleep(300);
return new InventoryResult(true, 10);
}
// 子任务3:创建运输标签
private static ShippingLabel createShippingLabel(String orderId) throws InterruptedException {
Thread.sleep(400);
return new ShippingLabel("LABEL-" + orderId, "北京市海淀区...");
}
// 嵌套任务:验证支付方式
private static boolean validatePaymentMethod(String orderId) throws InterruptedException {
Thread.sleep(200);
return true;
}
// 嵌套任务:处理交易
private static String processTransaction(String orderId) throws InterruptedException {
Thread.sleep(350);
return "TXN-" + System.currentTimeMillis();
}
}
java
订单处理结果: OrderProcessingResult[payment=PaymentResult[success=true, transactionId=TXN-1755851931388], inventory=InventoryResult[available=true, quantity=10], label=ShippingLabel[labelId=LABEL-ORDER-789, address=北京市海淀区...]]
嵌套优势:
- 形成清晰的任务层次结构,与业务逻辑匹配
- 取消操作会沿着层次结构传播,确保整体一致性
- 异常处理可以在不同层级分别处理,符合责任链模式
5.7 用户信息聚合服务
java
public class UserInfoAggregator {
record UserProfile(String basicInfo,
List<Order> orders,
List<Recommendation> recommendations,
SocialProfile socialProfile) {}
public UserProfile getUserProfile(String userId)
throws ExecutionException, InterruptedException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// 并行获取用户各类信息
Subtask<String> basicInfoTask = scope.fork(() ->
userService.getBasicInfo(userId));
Subtask<List<Order>> ordersTask = scope.fork(() ->
orderService.getUserOrders(userId));
Subtask<List<Recommendation>> recommendationsTask = scope.fork(() ->
recommendationService.getRecommendations(userId));
Subtask<SocialProfile> socialProfileTask = scope.fork(() ->
socialService.getSocialProfile(userId));
// 等待所有任务完成
scope.join();
scope.throwIfFailed(e -> {
// 转换异常为领域特定异常
if (e instanceof ServiceUnavailableException) {
return new ProfileServiceException("Service unavailable", e);
}
return new ProfileServiceException("Failed to fetch user profile", e);
});
// 组合所有结果
return new UserProfile(
basicInfoTask.get(),
ordersTask.get(),
recommendationsTask.get(),
socialProfileTask.get()
);
}
}
}
5.8 多镜像文件下载
java
public class FileDownloader {
public byte[] downloadFile(String fileId, List<String> mirrors)
throws ExecutionException, InterruptedException {
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<byte[]>()) {
// 同时从多个镜像下载
for (String mirror : mirrors) {
scope.fork(() -> downloadFromMirror(mirror, fileId));
}
scope.join();
// 返回第一个成功下载的内容
return scope.result();
} catch (Exception e) {
throw new DownloadException("All mirrors failed for file: " + fileId, e);
}
}
private byte[] downloadFromMirror(String mirrorUrl, String fileId) {
// 实现下载逻辑
// 抛出异常如果下载失败
return httpClient.download(mirrorUrl + "/" + fileId);
}
}
5.9 批量数据处理管道
java
public class DataProcessingPipeline {
public ProcessingResult processBatch(List<DataRecord> batch)
throws ExecutionException, InterruptedException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// 为每个记录创建处理任务
List<Subtask<RecordResult>> tasks = batch.stream()
.map(record -> scope.fork(() -> processRecord(record)))
.collect(Collectors.toList());
scope.join();
scope.throwIfFailed();
// 收集所有成功结果
List<RecordResult> results = tasks.stream()
.map(Subtask::get)
.collect(Collectors.toList());
return aggregateResults(results);
}
}
private RecordResult processRecord(DataRecord record) {
// 复杂的数据处理逻辑
validate(record);
transform(record);
return enrich(record);
}
}
六、结构化并发的生命周期管理
结构化并发的核心优势在于严格的生命周期管理,通过以下机制实现:
- 范围绑定 :所有任务在
StructuredTaskScope
的 try-with-resources 块中创建,确保自动关闭 - 取消传播:当一个任务失败或被取消,所有相关任务都会被自动取消
- 异常聚合:多个任务的异常会被聚合处理,避免遗漏
实例化StructuredTaskScope 调用fork()提交任务 调用join() 所有任务完成 任一任务失败/成功 取消剩余任务 所有任务终止 try-with-resources自动关闭 Created Active Joining AllCompleted Shutdown Canceling Closed
是 否 创建 StructuredTaskScope 使用 scope.fork 启动子任务 调用 scope.join 等待完成 是否有任务失败? 处理或抛出异常 收集所有任务结果 清理资源 返回最终结果
完整代码模板
java
public ReturnType structuredConcurrencyTemplate()
throws ExecutionException, InterruptedException {
// 1. 创建作用域(选择合适策略)
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// 2. 启动所有子任务
Subtask<Result1> task1 = scope.fork(() -> operation1());
Subtask<Result2> task2 = scope.fork(() -> operation2());
// ... 更多任务
// 3. 等待所有任务完成
scope.join();
// 4. 检查并处理失败
if (scope.state() == StructuredTaskScope.State.FAILED) {
// 自定义异常处理逻辑
handleFailure(scope);
}
// 5. 或者使用 throwIfFailed 自动传播异常
scope.throwIfFailed(e ->
new CustomBusinessException("Operations failed", e));
// 6. 收集并处理成功结果
return combineResults(
task1.get(),
task2.get()
// ... 其他任务结果
);
} // 7. 自动清理:确保所有子任务完成
}
八、最佳实践总结
8.1 任务范围总结
场景 | 推荐使用的范围类型 | 核心特点 |
---|---|---|
所有任务必须成功 | ShutdownOnFailure | 任一失败则全部取消 |
只需一个成功结果 | ShutdownOnSuccess | 任一成功则取消其他 |
自定义关闭逻辑 | 扩展 StructuredTaskScope | 实现自定义的终止策略 |
8.2 线程选择策略
- I/O 密集型任务 :优先使用虚拟线程(
Thread.ofVirtual().factory()
) - CPU 密集型任务:使用平台线程池,并行度不宜超过 CPU 核心数
- 混合任务:根据主要工作类型选择,或使用默认线程工厂
8.3 异常处理最佳实践
- 始终在
join()
后调用throwIfFailed()
(ShutdownOnFailure) - 使用 try-catch 块集中处理所有任务可能抛出的异常
- 子任务应抛出有意义的异常,便于上层处理
- 避免在子任务中捕获异常后不重新抛出,导致故障无法传播
8.4 性能优化建议
- 合理设置任务粒度:避免过细(任务调度开销大)或过粗(并行度不足)
- 利用虚拟线程处理大量短期任务,减少资源消耗
- 对长时间运行的任务,实现中断响应逻辑
- 避免在
join()
前阻塞,充分利用并行性
8.5 代码组织原则
- 每个
StructuredTaskScope
对应一个明确的业务功能 - 使用嵌套结构表达任务间的依赖关系
- 将相关任务的处理逻辑放在一起,提高可读性
- 提取重复的并发模式为工具方法
8.6 与传统并发方式的对比
特性 | 结构化并发 | 传统并发(ExecutorService) |
---|---|---|
任务生命周期 | 与代码块绑定,自动管理 | 手动管理,易泄漏 |
取消机制 | 自动传播到所有相关任务 | 需要手动追踪并取消 |
异常处理 | 集中式处理,自动聚合 | 分散处理,需手动协调 |
代码可读性 | 高,结构清晰 | 低,分散在多处 |
调试难度 | 低,层次结构明确 | 高,线程关系复杂 |
资源效率 | 高,配合虚拟线程更佳 | 中,线程创建成本高 |
学习曲线 | 中等,需理解新模型 | 低,但深入难 |