✅JDK25的ScopedValue是什么?为什么可以替代ThreadLocal?
典型回答
**ScopedValue**** 是 Java 25 中引入的一个API(JEP 429)**,它是一种能够在特定作用域内共享不可变数据的新机制,主要用于在线程内和跨线程之间安全、高效地传递数据。
它的核心思想是 “作用域” (Scope)。一个 ScopedValue 的值被绑定到一个动态定义的作用域上。一旦执行流程退出这个作用域,该绑定就会被自动撤销。这个作用域通常由 runWhere(ScopedValue, value, Runnable) 方法来定义。
// 1. 声明一个 ScopedValue(通常为 static final)
final static ScopedValue<String> USER_CONTEXT = ScopedValue.newInstance();
// 2. 在某个作用域内“绑定”一个值,并运行代码
ScopedValue.runWhere(USER_CONTEXT, "Hollis", () -> {
// 在这个 Lambda(作用域)内,USER_CONTEXT 的值是 "Hollis"
methodThatReadsUserContext();
});
// 3. 在作用域内的任何地方读取值
void methodThatReadsUserContext() {
String user = USER_CONTEXT.get(); // 获取到 "Hollis"
System.out.println("User is: " + user);
}
// 退出 runWhere 的代码块后,USER_CONTEXT 与 "Hollis" 的绑定自动失效ScopedValue 被设计用来解决 ThreadLocal 的一些长期存在的问题的,相比 ThreadLocal 他有几个优势:
**解决内存泄漏问题 **
ThreadLocal你需要手动管理其生命周期。调用set(value)后,你必须记得在 finally 块中调用remove(),否则数据会一直留在线程中,导致内存泄漏(尤其是在线程池中,线程会被复用)。
ScopedValue的生命周期严格限定在runWhere方法定义的作用域内。退出作用域后,绑定自动地被清除,无需手动移除,从根本上避免了内存泄漏的风险。
虚拟线程更友好
- ThreadLocal的内部使用线程内部的
ThreadLocalMap,也就意味着每个Thread内部维护了一个ThreadLocalMap。对于平台线程,因为不会太多,所以还可以接受,但是对于虚拟线程,那可能是成千上万的,那么有这么多个Map,就显得笨重了。 ScopedValue的值不存储在线程中,而是存储在作用域中。一个ScopedValue的绑定在其作用域内(即runWhere方法定义的代码块内)对所有在该作用域中运行的代码(包括所有子任务)都是可见的。**
父子线程间传递更友好
- ThreadLocal本身不支持主子线程之间的传递,需要借助InheritableThreadLocal实现
- 在一个
ScopedValue.runWhere作用域内,使用StructuredTaskScope.fork()(结构化并发,这个特性在JDK25中还是预览版)创建的所有子任务(虚拟线程)自动就能够读取到父作用域中绑定的ScopedValue。
性能更好
- ThreadLocal内部使用线程内部的
ThreadLocalMap,这是一个哈希表结构。get()和set()操作涉及哈希查找,虽然很快,但在极端高性能场景下仍有开销。 - JVM 可以将
ScopedValue的读取优化为类似局部变量访问的速度,因为它基于不可变且范围限定的数据。