Scoped Values 是 JDK 21 中引入的一个预览特性,旨在提供一种线程安全的值传递机制,特别适合在 虚拟线程 中使用。它是 ThreadLocal 的一种现代化替代方案,解决了 ThreadLocal 在高并发和虚拟线程场景中的一些局限性。
Scoped Values 是什么?
Scoped Values 是一种新的 API,允许在特定的代码块中定义和访问线程范围内的值。它的作用域是明确的,值的生命周期与代码块绑定,而不是与线程绑定。
- 类名:
jdk.incubator.concurrent.ScopedValue - 包名:
jdk.incubator.concurrent
Scoped Values 提供了一种更高效、更安全的方式来在线程之间传递不可变数据,特别是在使用虚拟线程时。
Scoped Values 的核心特性
-
线程安全:
- Scoped Values 是线程安全的,适合在并发场景中使用。
- 它避免了
ThreadLocal的一些常见问题(如内存泄漏)。
-
作用域明确:
- Scoped Values 的值仅在特定的代码块中有效,超出作用域后会自动失效。
-
不可变性:
- Scoped Values 的值是不可变的,确保了线程间数据的安全性。
-
适合虚拟线程:
- Scoped Values 专为虚拟线程设计,避免了
ThreadLocal在虚拟线程中的性能问题。
- Scoped Values 专为虚拟线程设计,避免了
Scoped Values 与 ThreadLocal 的区别
| 特性 | Scoped Values | ThreadLocal |
|---|---|---|
| 绑定方式 | 与代码块绑定 | 与线程绑定 |
| 线程安全 | 是 | 是 |
| 适合虚拟线程 | 是 | 否(性能较差) |
| 值的可变性 | 不可变 | 可变 |
| 内存泄漏风险 | 无 | 有(如果未正确清理) |
| 使用场景 | 高并发、虚拟线程 | 传统线程场景 |
Scoped Values 的使用场景
-
虚拟线程中的上下文传递:
- 在虚拟线程中传递用户信息、请求上下文等。
-
替代 ThreadLocal:
- 替代
ThreadLocal,避免内存泄漏和性能问题。
- 替代
-
作用域明确的值传递:
- 在特定代码块中传递值,确保值的生命周期与代码块绑定。
Scoped Values 的使用示例
1. 基本使用
import jdk.incubator.concurrent.ScopedValue;
public class ScopedValueExample {
// 定义 ScopedValue
static final ScopedValue<String> USER = ScopedValue.newInstance();
public static void main(String[] args) {
// 在作用域中设置 ScopedValue 的值
ScopedValue.where(USER, "Alice").run(() -> {
System.out.println("Current user: " + USER.get()); // 输出 "Alice"
});
// 超出作用域后,ScopedValue 的值不可访问
System.out.println("Outside scope: " + USER.get()); // 抛出 IllegalStateException
}
}
输出:
Current user: Alice
Exception in thread "main" java.lang.IllegalStateException: No value present
2. 在虚拟线程中使用
import jdk.incubator.concurrent.ScopedValue;
import java.util.concurrent.Executors;
public class VirtualThreadScopedValueExample {
static final ScopedValue<String> REQUEST_ID = ScopedValue.newInstance();
public static void main(String[] args) throws InterruptedException {
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());
});
});
}
}
}
}
输出(示例):
Thread: VirtualThread[#1]/runnable@ForkJoinPool-1-worker-1, Request ID: Request-0
Thread: VirtualThread[#2]/runnable@ForkJoinPool-1-worker-2, Request ID: Request-1
Thread: VirtualThread[#3]/runnable@ForkJoinPool-1-worker-3, Request ID: Request-2
Thread: VirtualThread[#4]/runnable@ForkJoinPool-1-worker-4, Request ID: Request-3
Thread: VirtualThread[#5]/runnable@ForkJoinPool-1-worker-5, Request ID: Request-4
3. 嵌套作用域
Scoped Values 支持嵌套作用域,内层作用域可以覆盖外层作用域的值。
import jdk.incubator.concurrent.ScopedValue;
public class NestedScopedValueExample {
static final ScopedValue<String> USER = ScopedValue.newInstance();
public static void main(String[] args) {
ScopedValue.where(USER, "Alice").run(() -> {
System.out.println("Outer scope user: " + USER.get()); // 输出 "Alice"
ScopedValue.where(USER, "Bob").run(() -> {
System.out.println("Inner scope user: " + USER.get()); // 输出 "Bob"
});
System.out.println("Back to outer scope user: " + USER.get()); // 输出 "Alice"
});
}
}
输出:
Outer scope user: Alice
Inner scope user: Bob
Back to outer scope user: Alice
Scoped Values 的好处
-
线程安全:
- Scoped Values 是线程安全的,适合高并发场景。
-
作用域明确:
- 值的生命周期与代码块绑定,避免了
ThreadLocal的内存泄漏问题。
- 值的生命周期与代码块绑定,避免了
-
适合虚拟线程:
- Scoped Values 专为虚拟线程设计,避免了
ThreadLocal在虚拟线程中的性能问题。
- Scoped Values 专为虚拟线程设计,避免了
-
简化上下文传递:
- Scoped Values 提供了一种简单的方式在代码块中传递上下文数据。
Scoped Values 的局限性
-
仅支持不可变值:
- Scoped Values 的值是不可变的,无法在作用域内修改。
-
需要 JDK 21:
- Scoped Values 是 JDK 21 的预览特性,使用时需要显式启用。
如何启用 Scoped Values
Scoped Values 是 JDK 21 的预览特性,使用时需要在运行时启用预览功能:
java --enable-preview -cp . YourMainClass
总结
Scoped Values 是 JDK 21 中引入的一个现代化特性,专为虚拟线程设计,提供了一种线程安全、作用域明确的值传递机制。它的主要优势包括:
- 替代
ThreadLocal,避免内存泄漏。 - 适合高并发和虚拟线程场景。
- 简化上下文数据的传递。
Scoped Values 是 Java 并发编程的一次重要改进,特别适合现代化的高并发应用程序。