Java 24 introduces Scoped Values, a powerful alternative to ThreadLocal
that offers better performance and cleaner code for managing per-thread data. This is a preview language feature.
Here's an example of ScopedValue
in action:
private static final ScopedValue<String> USER_ID = ScopedValue.newInstance(); public void handle(Request req, String userId) { ScopedValue.where(USER_ID, userId) .run(() -> handle(req)); } private void handle(Request req) { String data = getData(req); // Do something else } private String getData(Request req) { return runQuery(req, USER_ID.get()); }
As shown above, ScopedValue
provides a means to pass data (the userId
) securely to a faraway method without using method parameters. The faraway method can access the data via the ScopedValue
object. This eliminates the need to pass additional parameters explicitly through multiple method calls.
- Scoped Values are immutable once set inside
ScopedValue.where(...)
. On the other hand,ThreadLocal
allows values to be changed at any time, which can lead to inconsistent state across different parts of a request. - Scoped Values are automatically removed after the scope ends, whereas
ThreadLocal
requires an explicit call toremove()
to avoid memory leaks, especially in thread pools. - Scoped Values bind data to a specific execution scope, ensuring that when a new task starts on a thread, it doesn’t inherit values from a previous request.
ThreadLocal
stores data at the thread level, meaning values persist across multiple tasks when using a thread pool. - Scoped Values work well with virtual threads and structured concurrency APIs.
For comparison, here's the same example using ThreadLocal
:
private static final ThreadLocal<String> USER_ID = new ThreadLocal<>(); public void handle(Request req, String userId) { try { USER_ID.set(userId); handle(req); } finally { USER_ID.remove(); // to prevent memory leaks } } private void handle(Request req) { String data = getData(req); // Do something else } private String getData(Request req) { return runQuery(req, USER_ID.get()); }
While ThreadLocal
still has its uses, most new applications will benefit from Scoped Values’ immutability, automatic cleanup, and better thread management.