在 Java 25 中引入的Scoped Values是一种新的线程上下文传递机制,旨在替代传统的ThreadLocal,尤其适用于虚拟线程(Virtual Threads)场景。它解决了ThreadLocal在虚拟线程中存在的内存泄漏风险、状态污染和性能问题,提供了更安全、更轻量的线程局部状态管理方式。
Scoped Values 的核心特性
作用域限定:值的生命周期被严格限定在一个代码块(作用域)内,超出作用域后自动失效,避免内存泄漏。
不可变性:通常通过final定义,一旦在作用域中设置,无法被修改,保证线程安全。
可继承性:子线程(包括虚拟线程)可以自动继承父线程的 Scoped Values,无需手动传递上下文。
高效性:专为虚拟线程优化,比ThreadLocal更轻量,减少线程切换的开销。
与 ThreadLocal 的对比
ThreadLocal的问题在于:
线程池复用线程时,若未手动清理,会导致状态污染;
虚拟线程数量庞大时,ThreadLocal的哈希表实现会带来内存和性能压力;
可变性导致线程安全风险。
而 Scoped Values 通过作用域绑定和不可变性从根源上解决了这些问题。
应用实例:请求上下文传递
在 Web 服务中,常需要在请求处理的整个链路(可能涉及多个虚拟线程)中传递上下文信息(如用户 ID、请求 ID)。使用 Scoped Values 可以优雅地实现这一需求。
代码示例
import java.util.concurrent.Executors;
// 1. 定义Scoped Values(通常为静态常量)
public class ScopedValuesExample {
// 定义两个Scoped Value:用户ID和请求ID
private static final ScopedValue<String> USER_ID = ScopedValue.newInstance();
private static final ScopedValue<String> REQUEST_ID = ScopedValue.newInstance();
public static void main(String[] args) {
// 模拟Web请求入口:设置Scoped Values的作用域
String userId = "user_123";
String requestId = "req_456";
// 2. 通过ScopedValue.where()绑定值到作用域,在作用域内的代码(包括子线程)可访问
ScopedValue.where(USER_ID, userId)
.where(REQUEST_ID, requestId)
.run(() -> handleRequest()); // 作用域内的核心逻辑
}
// 处理请求的核心方法
private static void handleRequest() {
System.out.println("主线程处理请求 - 用户ID: " + USER_ID.get() + ", 请求ID: " + REQUEST_ID.get());
// 3. 在虚拟线程中访问父线程的Scoped Values
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
// 子任务1:获取用户信息
executor.submit(() -> {
System.out.println("虚拟线程1 - 获取用户信息: " + USER_ID.get());
return null;
});
// 子任务2:记录请求日志
executor.submit(() -> {
System.out.println("虚拟线程2 - 记录日志: " + REQUEST_ID.get());
return null;
});
}
}
}
本文系作者在时代Java发表,未经许可,不得转载。
如有侵权,请联系nowjava@qq.com删除。