JDK21 升级之 Scoped Values

749 阅读4分钟

Scoped Values 是 JDK 21 中引入的一个预览特性,旨在提供一种线程安全的值传递机制,特别适合在 虚拟线程 中使用。它是 ThreadLocal 的一种现代化替代方案,解决了 ThreadLocal 在高并发和虚拟线程场景中的一些局限性。


Scoped Values 是什么?

Scoped Values 是一种新的 API,允许在特定的代码块中定义和访问线程范围内的值。它的作用域是明确的,值的生命周期与代码块绑定,而不是与线程绑定。

  • 类名jdk.incubator.concurrent.ScopedValue
  • 包名jdk.incubator.concurrent

Scoped Values 提供了一种更高效、更安全的方式来在线程之间传递不可变数据,特别是在使用虚拟线程时。


Scoped Values 的核心特性

  1. 线程安全

    • Scoped Values 是线程安全的,适合在并发场景中使用。
    • 它避免了 ThreadLocal 的一些常见问题(如内存泄漏)。
  2. 作用域明确

    • Scoped Values 的值仅在特定的代码块中有效,超出作用域后会自动失效。
  3. 不可变性

    • Scoped Values 的值是不可变的,确保了线程间数据的安全性。
  4. 适合虚拟线程

    • Scoped Values 专为虚拟线程设计,避免了 ThreadLocal 在虚拟线程中的性能问题。

Scoped Values 与 ThreadLocal 的区别

特性Scoped ValuesThreadLocal
绑定方式与代码块绑定与线程绑定
线程安全
适合虚拟线程否(性能较差)
值的可变性不可变可变
内存泄漏风险有(如果未正确清理)
使用场景高并发、虚拟线程传统线程场景

Scoped Values 的使用场景

  1. 虚拟线程中的上下文传递

    • 在虚拟线程中传递用户信息、请求上下文等。
  2. 替代 ThreadLocal

    • 替代 ThreadLocal,避免内存泄漏和性能问题。
  3. 作用域明确的值传递

    • 在特定代码块中传递值,确保值的生命周期与代码块绑定。

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 的好处

  1. 线程安全

    • Scoped Values 是线程安全的,适合高并发场景。
  2. 作用域明确

    • 值的生命周期与代码块绑定,避免了 ThreadLocal 的内存泄漏问题。
  3. 适合虚拟线程

    • Scoped Values 专为虚拟线程设计,避免了 ThreadLocal 在虚拟线程中的性能问题。
  4. 简化上下文传递

    • Scoped Values 提供了一种简单的方式在代码块中传递上下文数据。

Scoped Values 的局限性

  1. 仅支持不可变值

    • Scoped Values 的值是不可变的,无法在作用域内修改。
  2. 需要 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 并发编程的一次重要改进,特别适合现代化的高并发应用程序。