jdk21中的ScopedValue

2,138 阅读2分钟

前言

ScopedValue是jdk21引入新属性,用来代替ThreadLocal的线程传值,ScopedValue目前处于预览状态,将支持在线程内和线程间共享不可变数据。它们优于线程局部变量,尤其是在使用大量虚拟线程时

jdk21使用虚拟线程

jdk21在使用虚拟线程时,需要开启预览状态,以下是maven开启预览状态


 <build>
       <plugins>
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-compiler-plugin</artifactId>
               <configuration>
                   <compilerArgs>
                       <compilerArg>--enable-preview</compilerArg>
                   </compilerArgs>
               </configuration>
           </plugin>
       </plugins>
    </build>

或者在ide启动时,加入参数的预览

ScopedValue使用

where方法

where方法的参数为:

public static <T> Carrier where(ScopedValue<T> key, T value)

value是要获取的值,把当前值绑定在当前线程中 示例为:

public class ScopedValue1Demo {

    private static ScopedValue<String> stringScopedValue = ScopedValue.newInstance();

    public static void main(String[] args) {
        ScopedValue.where(stringScopedValue, say()).run(() -> {
            String result = stringScopedValue.get();
            System.out.println(result);
            hello();
        });
        System.out.println(stringScopedValue.get());
    }


    public static String say() {
        System.out.println("============say()方法");
        return "aaa";
    }


    public static void hello() {
        System.out.println(stringScopedValue.get());
    }
}


image.png 最后在main方法获取参数会报异常

runWhere使用

runwhere的方法参数为:

  public static <T> void runWhere(ScopedValue<T> key, T value, Runnable op)
  
  

示例为:


public class ScopedValueDemo {


    private static ScopedValue<String> stringScopedValue = ScopedValue.newInstance();

    public static void main(String[] args) {
        ScopedValue.runWhere(stringScopedValue, "aaa", () -> {
                    System.out.println(stringScopedValue.get());
                }
        );
    }
}


callWhere使用

public static <T, R> R callWhere(ScopedValue<T> key,
                                     T value,
                                     Callable<? extends R> op) throws Exception

示例为:

public class ScopedValue3Demo {

    private static ScopedValue<String> stringScopedValue = ScopedValue.newInstance();

    public static void main(String[] args) throws Exception {
        String result = ScopedValue.callWhere(stringScopedValue, say(), ()->{
            System.out.println(12);
            System.out.println(stringScopedValue.get());
            return "aaa";
        });
        System.out.println(result);
    }


    public static String say() {
        System.out.println("============say()方法");
        return "aaa";
    }
}


线程传值问题


import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope;

public class ScopedValue4Demo {

    private static ScopedValue<String> stringScopedValue = ScopedValue.newInstance();

    public static void main(String[] args) {
        ScopedValue.runWhere(stringScopedValue, "aaaa", () -> {
            try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
                StructuredTaskScope.Subtask<String> user = scope.fork(() -> {
                    System.out.println("hello:" + stringScopedValue.get());
                    return "hello";
                });
                StructuredTaskScope.Subtask<Integer> order = scope.fork(() -> {
                    System.out.println("say():" + stringScopedValue.get());
                    return 12;
                });
                try {
                    scope.join().throwIfFailed();
                } catch (ExecutionException | InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }
}


总结

ThreadLocal使用的时候,需要remove参数,但是ScopedValue在使用时,是不需要remove参数的,ScopedValue 具备 ThreadLocal 的核心特征,也就是每个线程只有一个值。与 ThreadLocal 不同的是,ScopedValue 是不可变的,并且有确定的作用域,但是同样的,ThreadLocal也能在线程中使用,这个看个人习惯