ScopedValue 是 Java 21 引入的预览 API(JEP 429),用于在特定作用域内安全共享不可变数据。它旨在解决传统 ThreadLocal 在虚拟线程(Virtual Thread)和高并发场景中的局限性,如内存泄漏、生命周期管理复杂等问题。
作用域绑定
值仅在代码块或方法调用栈中有效,超出作用域后自动失效,避免手动清理。
不可变性
一旦绑定,值不可修改,确保线程安全,防止并发问题。
高效性
访问开销约 3ns/次,远低于 ThreadLocal 的 15ns/次,适合高并发场景。
虚拟线程友好
专为虚拟线程设计,避免 ThreadLocal 在虚拟线程中的性能问题。
|
1 2 3 4 5 6 |
import java.lang.ScopedValue;
public class ScopedValueExample { // 定义一个静态最终的 ScopedValue 对象 static final ScopedValue<String> USER = ScopedValue.newInstance(); } |
|
1 2 3 |
ScopedValue.where(USER, "Alice").run(() -> { System.out.println("Current User: " + USER.get()); // 输出 "Alice" }); |
|
1 2 3 4 5 |
try { System.out.println("Outside Scope: " + USER.get()); } catch (IllegalStateException e) { System.out.println("无法在作用域外访问 ScopedValue"); } |
内层作用域可覆盖外层值:
|
1 2 3 |
ScopedValue.where(USER, "Bob").run(() -> { System.out.println("Inner Scope: " + USER.get()); // 输出 "Bob" }); |
|
1 2 3 4 5 6 7 8 9 10 11 |
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { for (int i = 0; i < 5; i++) { int id = i; executor.submit(() -> { ScopedValue.where(REQUEST_ID, "Request-" + id).run(() -> { System.out.println("Thread: " + Thread.currentThread() + ", Request ID: " + REQUEST_ID.get()); }); }); } } |
虚拟线程中的上下文传递
如用户会话、请求 ID 等,确保每个虚拟线程独立访问数据。
替代 ThreadLocal
避免内存泄漏和手动清理,例如在 Web 框架中传递请求级数据。
高并发系统
在分布式系统中传递事务 ID 或跟踪 ID,简化日志上下文管理。
| 特性 | ThreadLocal | ScopedValue |
|---|---|---|
| 值绑定 | 线程本身 | 代码块或方法调用栈 |
| 生命周期 | 与线程生命周期一致 | 与作用域绑定,自动失效 |
| 可变性 | 可变,需手动清理 | 不可变,自动管理 |
| 性能 | 15ns/次访问 | 3ns/次访问 |
| 虚拟线程支持 | 不支持(可能导致内存泄漏) | 完美支持 |
根据具体技术栈选择合适的工具,Java 开发者应优先掌握 ScopedValue 以应对高并发挑战