Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Справочник Java

ScopedValue

Значение, доступное коду только на время вызова — например, текущий пользователь запроса. Замена ThreadLocal.

Доступно с: Java 25

Синтаксис

public final class ScopedValue<T>

Примеры

Значение привязывается через where(...).run(...) и читается через get() в любом вызванном методе:

static final ScopedValue<String> USER = ScopedValue.newInstance();
 
void handle() {
    IO.println("Handling request for " + USER.get());
}
 
void main() {
    ScopedValue.where(USER, "Anna").run(this::handle);
    IO.println(USER.isBound());
}

Результат:

Handling request for Anna
false

call() возвращает результат, а вложенная привязка временно заменяет значение:

static final ScopedValue<String> USER = ScopedValue.newInstance();
 
String greet() {
    return "Hello, " + USER.orElse("guest");
}
 
void main() {
    IO.println(greet());
    IO.println(ScopedValue.where(USER, "Oleg").call(this::greet));
 
    ScopedValue.where(USER, "Anna").run(() -> {
        IO.println(greet());
        ScopedValue.where(USER, "Mia").run(() -> IO.println(greet()));
        IO.println(greet());
    });
}

Результат:

Hello, guest
Hello, Oleg
Hello, Anna
Hello, Mia
Hello, Anna

Чтение вне привязки:

static final ScopedValue<String> USER = ScopedValue.newInstance();
 
void main() {
    USER.get();
}

Результат:

Exception in thread "main" java.util.NoSuchElementException: ScopedValue not bound

Примечания

В отличие от ThreadLocal, значение нельзя изменить — только привязать заново на время вложенного вызова, а после выхода из run() оно исчезает само. Так данные не «утекают» между запросами и не требуют ручной очистки. Обычно объект объявляют как static final поле. Значение видно в дочерних потоках StructuredTaskScope (структурный параллелизм, в Java 25 — превью). Финальная версия API — Java 25.

См. также

  • Thread.currentThread() — Возвращает поток, который выполняет текущий код.
  • Thread.ofVirtual() — Создаёт виртуальный поток — лёгкий поток, которых можно запустить миллионы.
  • Executors.newVirtualThreadPerTaskExecutor() — Создаёт сервис, который запускает каждую задачу в отдельном виртуальном потоке.
  • Runnable — Задача без аргументов и без результата.

В учебнике

  • Ограниченные значения (ScopedValue) — ScopedValue в Java 25 — как передать данные вглубь вызовов без параметров, ScopedValue.newInstance, where(...).run(...), get, вложенная привязка и почему они заменяют ThreadLocal в виртуальных потоках.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.