Gymterview
middle

Scoped Values — замена ThreadLocal?

Scoped Values — механизм для передачи иммутабельных данных через стек вызовов без явной передачи параметров. Альтернатива ThreadLocal, разработанная специально для Virtual Threads — не создаёт копию на каждый поток и автоматически ограничивает время жизни.

ThreadLocal vs ScopedValue

Критерий ThreadLocal ScopedValue
Мутабельность Мутабельный (set()) Immutable (только where().run())
Время жизни Пока не вызван remove() Автоматически — ограничен run()/call()
Наследование InheritableThreadLocal (копия) Автоматическое наследование в child-scope
Память Копия на каждый поток Разделяемая ссылка
Virtual Threads Проблема (миллион копий = OOM) Оптимально
Пример
// ThreadLocal — проблемы с Virtual Threads:
private static final ThreadLocal<User> CURRENT_USER = new ThreadLocal<>();
CURRENT_USER.set(user);
// ... где-то в стеке вызовов:
User u = CURRENT_USER.get();
CURRENT_USER.remove(); // легко забыть!

// ScopedValue — решение:
private static final ScopedValue<User> CURRENT_USER = ScopedValue.newInstance();

ScopedValue.where(CURRENT_USER, authenticatedUser)
    .run(() -> {
        // CURRENT_USER доступен во всём этом scope
        processRequest();
    });

// В вызываемом коде:
void processRequest() {
    User user = CURRENT_USER.get(); // доступно
    // user автоматически недоступен после выхода из run()
}

Частые ошибки

  • Вызвать get() вне run()NoSuchElementException; ScopedValue доступен только в scope
  • Использовать ThreadLocal с миллионами Virtual Threads — OOM; мигрируйте на ScopedValue
  • ScopedValue для мутабельного состояния — ScopedValue immutable; для мутабельного состояния нужен другой подход

Как используется в 2026

  • ScopedValue финализирован в Java 25
  • Используется для propagation authentication context, trace ID, request metadata
  • Spring Security и Micrometer начинают поддержку ScopedValue для Virtual Threads

На собеседовании: объясните три проблемы ThreadLocal с Virtual Threads (мутабельность, утечки, память) и как ScopedValue решает каждую. Частая ошибка — не знать про автоматическое наследование в child-scope при Structured Concurrency.