一、作用域值定义

    作用域值可以在同一个线程的多个方法之间方便地共享数据。相对于传统的ThreadLocal来说,更安全,同时权限控制可以精确到方法级别。

二、核心特性


1. 不可变性:
        ScopedValues的值一旦设置,就不能更改。这确保了数据的安全性和一致性。
2.作用域限制:
        ScopedValues的生命周期严格限制在其定义的作用域内,超出作用域后无法访问。
3.支持虚拟线程:
        ScopedValues与虚拟线程无缝集成,适合高并发场景。
4.无副作用:
        由于ScopedValues是不可变的,它们不会引入传统线程局部变量可能带来的副作用。

三、ThreadLocal 与Scoped Values 的对比

public class Main1 {
    private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();
    public static void main(String[] args) {
        threadLocal.set("张三");
        run_1();

        //可以获取
        System.out.println("main:" + threadLocal.get());

    }

    private static void run_1() {
        System.out.println("run_1:" + threadLocal.get());
        // 问题:必须手动清理,容易忘记清理,导致内存泄露
        threadLocal.remove();
        run_2();
    }
    private static void run_2() {
        System.out.println("run_2:" + threadLocal.get());
    }
}

                                          

import java.lang.ScopedValue;

public class Main {

    //值封装在ScopedValue中
    private static final  ScopedValue<String>  CURRENT_USER = ScopedValue.newInstance();
    public static void main(String[] args) {
        //在作用域内获取当前用户
      ScopedValue.runWhere(CURRENT_USER, "李四", Main::run_1);

        //不在作用域内,不能获取数据
        //发生异常:Exception in thread "main" java.util.NoSuchElementException
      //  System.out.println("main:" + CURRENT_USER.get());

    }

    public  static void run_1(){
        System.out.println("run_1:" + CURRENT_USER.get());
        ScopedValue.runWhere(CURRENT_USER, "张三", Main::run_2);
    }

    public  static void run_2(){
        System.out.println("run_2:" + CURRENT_USER.get());
    }
}

 

四、适用场景

  • 虚拟线程中的上下文传递
    如用户会话、请求 ID 等,确保每个虚拟线程独立访问数据。

  • 替代 ThreadLocal
    避免内存泄漏和手动清理,例如在 Web 框架中传递请求级数据。

  • 高并发系统
    在分布式系统中传递事务 ID 或跟踪 ID,简化日志上下文管理。

案例1:日志上下文管理

public class Test2 {

    // 定义一个静态最终的 ScopedValue 对象
    private static final  ScopedValue<String>  CURRENT_USER = ScopedValue.newInstance();
    public static void main(String[] args) {
        System.out.println("=== Scoped Values  ===");

        // 场景1:管理员操作
        ScopedValue.where(CURRENT_USER, "管理员Alice")
                .run(() -> performSecureOperation());

        // 场景2:普通用户操作
        ScopedValue.where(CURRENT_USER, "普通用户Bob")
                .run(() -> performSecureOperation());

        System.out.println("=== 结束 ===");
    }

    private static void performSecureOperation() {
        // 在作用域内获取当前用户
        String user = CURRENT_USER.get();
        System.out.println("当前执行用户: " + user);

        // 调用深层方法,无需传递用户参数
        logOperation();
        validateAccess();
    }

    private static void logOperation() {
        // 任何被调用的方法都可以访问作用域值
        String user = CURRENT_USER.get();
        System.out.println("[" + user + "] 执行了安全操作");
    }

    private static void validateAccess() {
        String user = CURRENT_USER.get();
        if (user.startsWith("管理员")) {
            System.out.println("权限验证通过: " + user + " 拥有完全访问权限");
        } else {
            System.out.println("权限验证通过: " + user + " 拥有基础访问权限");
        }
    }
}

案例2:虚拟线程中的上下文传递

package org.hlx;

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class MyScopeValue3 {

    private static final ScopedValue USER_SESSION = ScopedValue.newInstance();
    private static final ScopedValue<String> REQUEST_ID = ScopedValue.newInstance();
    public static void main(String[] args) {

        //虚拟线程中的使用示例
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            // 提交多个任务,每个任务都有自己的作用域
            for (int i = 0; i < 5; i++) {
                final int taskId = i;
                executor.submit(() -> {
                    UserSession taskSession = new UserSession(
                            "task-user-" + taskId,
                            "任务用户" + taskId,
                            "user"
                    );

                    ScopedValue.where(USER_SESSION, taskSession)
                            .where(REQUEST_ID, "task-req-" + taskId)
                            .run(() -> {
                                System.out.println("虚拟线程 " + Thread.currentThread() +
                                        " 处理请求 " + REQUEST_ID.get() +
                                        " 处理用户: " + ((UserSession)USER_SESSION.get()).getUsername());
                            });
                });
            }

            // 等待所有任务完成
            executor.shutdown();
            executor.awaitTermination(1, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }


        System.out.println();
    }

package org.hlx;

/*
 * 用户会话类
 */
public  class UserSession {
    private  String userId;
    private  String username;
    private  String role;

    public UserSession(String userId, String username, String role) {
        this.userId = userId;
        this.username = username;
        this.role = role;
    }

    public String getUserId() {
        return userId;
    }

    public String getUsername() {
        return username;
    }

    public String getRole() {
        return role;
    }
}

总之,Java ScopedValue适用于高并发、虚拟线程场景,提供安全、高效的作用域值管理,是 ThreadLocal 的现代替代方案。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐