Working with Scoped Values JAVA 21+ PREVIEW

1. Understanding Scoped Values

AspectDetail
PurposePass immutable data implicitly to inner methods
ReplacesThreadLocal in many cases
LifetimeBounded by enclosing scope (lexical)

2. Creating Scoped Values

FormDetail
Declarationstatic final ScopedValue<User> CURRENT_USER = ScopedValue.newInstance();
Usually static finalLike a typed key

3. Binding Values

Example: Binding

ScopedValue.where(CURRENT_USER, user)
    .where(REQUEST_ID, id)
    .run(() -> processRequest());
MethodDetail
where(sv, val)Bind value
run(runnable)Execute body
call(callable)Execute and return

4. Reading Scoped Values

MethodDetail
sv.get()Returns bound value or throws
sv.isBound()Check before reading
sv.orElse(default)Default if unbound

5. Scoping Rules

RuleDetail
ImmutableCannot reassign within scope
LexicalAvailable only inside run/call
InheritanceForked vthreads in same scope inherit

6. Comparing with ThreadLocal

AspectScopedValueThreadLocal
MutabilityImmutableMutable
LifetimeBoundedUntil explicit remove
MemoryPer-scope, lightPer-thread, heavy with vthreads
InheritanceAutomatic via structured concurrencyVia InheritableThreadLocal

7. Using with Virtual Threads

AspectDetail
Memory efficientCritical with millions of vthreads
No leaksAuto cleared after scope ends

8. Inheritance in Structured Concurrency

AspectDetail
Auto-inheritedSubtasks forked in scope see all bindings
OverrideRe-bind in child scope

9. Best Practices

PracticeReason
Use for context propagationTracing IDs, security principals, locales
Don't store mutable objectsDefeats purpose
Prefer over ThreadLocalEspecially with virtual threads

10. Migration Strategies

StepDetail
Identify ThreadLocalsUsed for request-scoped data
ConvertReplace with ScopedValue
Wrap entry pointsBind at request boundary
Audit get sitesReplace with sv.get()