Java 21引入的ScopedValue是结构化并发中的关键API,为线程间数据传递提供了安全高效的解决方案。ScopedValue:Java 21 引入的结构化作用域值核心概念什么是 ScopedValue?作为线程安全且不可变的值容器,ScopedValue实现了动态作用域内的数据共享,避免了显式方法参数传递的繁琐。其显著特点包括:
隐式参数机制允许数据穿透中间方法直达目标
生命周期与执行周期严格绑定,自动清除
专为多线程环境优化,完美适配虚拟线程
值本身不可变,但可通过ScopedValue.Mutable封装可变状态
为什么需要 ScopedValue?传统方式的局限性在以下对比中显而易见:// 方式1:层层传递参数(代码冗长)
void handleRequest(HttpRequest req) {
String requestId = req.getId();
processStep1(requestId); // 每层都需要传
processStep2(requestId);
processStep3(requestId);
}// 方式2:ThreadLocal(生命周期难管理、易泄漏)
private static final ThreadLocal CONTEXT = new ThreadLocal();
void handleRequest(HttpRequest req) {
CONTEXT.set(req.getId());
try {
processStep1();
processStep2();
} finally {
CONTEXT.remove(); // 必须手动清理!
}
}
相较之下,ScopedValue展现出明显优势:// 定义 ScopedValue(静态 final)
private static final ScopedValue REQUEST_ID =
ScopedValue.newInstance();// 绑定值并执行
ScopedValue.runWhere(REQUEST_ID, "req-123", () -> {
processStep1(); // 可直接读取 REQUEST_ID
processStep2();
processStep3();
}); // 自动清除绑定
API 使用1. 创建 ScopedValue// 不可变值
ScopedValue NAME = ScopedValue.newInstance();// 可封装可变状态(推荐用 record)
record UserContext(String userId, String traceId) {}
ScopedValue CONTEXT = ScopedValue.newInstance();
2. 绑定值并执行// 方式1:runWhere (Runnable)
ScopedValue.runWhere(SCOPED_VALUE, "hello", () -> {
System.out.println(SCOPED_VALUE.get()); // "hello"
});// 方式2:callWhere (Callable,有返回值)
String result = ScopedValue.callWhere(SCOPED_VALUE, "data",
() -> someService.process()
);// 方式3:where (接受 Runnable/Callable/Function)
Consumer task = ScopedValue.where(SCOPED_VALUE, "val",
() -> doWork()
);
3. 读取值// 在绑定作用域内读取
String val = SCOPED_VALUE.get(); // 返回绑定的值// 获取 Optional(未绑定时返回空)
Optional opt = SCOPED_VALUE.orElse(null);// 检查是否绑定
boolean isBound = SCOPED_VALUE.isBound();
4. 映射与转换// 映射到另一个 ScopedValue
ScopedValue LENGTH = NAME.map(String::length);
ScopedValue.runWhere(NAME, "hello", () -> {
System.out.println(LENGTH.get()); // 5
});// flatMap(用于嵌套 Optional)
ScopedValue OPT = NAME.map(v -> Optional.of(v));
ScopedValue FLAT = OPT.flatMap(Optional::stream);
生命周期与作用域作用域边界ScopedValue KV = ScopedValue.newInstance();void