The 30 second answer
A scoped value shares read-only data, like the current user or request id, with every method called inside a bounded block, without passing it as a parameter. It's a safer ThreadLocal: callees can't change it, it disappears automatically when the block ends, and child tasks inherit it cheaply. It was a preview in Java 21 and became final later, in Java 25.
static final ScopedValue<User> CURRENT_USER = ScopedValue.newInstance();
void handle(Request req) {
ScopedValue.where(CURRENT_USER, authenticate(req))
.run(() -> service.process()); // bound only inside run()
}
void process() {
User u = CURRENT_USER.get(); // readable anywhere below
}| ThreadLocal | ScopedValue | |
|---|---|---|
| Mutability | set() anywhere | immutable binding; rebind only in a nested scope |
| Lifetime | until remove() or the thread ends (leak risk in pools) | ends with the scope |
| Child threads | InheritableThreadLocal copies values | shared cheaply with structured-concurrency subtasks |
Status: preview in Java 21 (JEP 446), further previews in 22 to 24, final in Java 25 (JEP 506).
Sources: JEP 446: Scoped Values (Preview), JEP 506: Scoped Values
Sources (0)
See inline citations in the answer above.