Skip to content

Java 25: Scoped Values vs ThreadLocal (JEP 506) - #100

Merged
mperor merged 1 commit into
masterfrom
DAILY/87-scoped-values-vs-threadlocal
Oct 23, 2026
Merged

mperor merged 1 commit into
masterfrom
DAILY/87-scoped-values-vs-threadlocal

Conversation

@mperor

@mperor mperor commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

What you'll learn

  • How to bind a value for a thread with ScopedValue.where(KEY, value).run(...) / .call(...), and that it is only visible inside that call
  • Why isBound() and get() reflect the binding's lifetime, throwing NoSuchElementException once it's gone
  • How nesting where(...).run(...) rebinds a ScopedValue only for the nested call, restoring the outer value afterward
  • Why ScopedValue has no set()/remove(), unlike the mutable, leak-prone ThreadLocal

Why it matters

ScopedValue (finalized in JDK 25 via JEP 506) gives you immutable, structured context propagation without the risk of forgetting a ThreadLocal.remove() in a pooled thread, and it composes safely with virtual threads and structured concurrency.

Read more

JEP 506: Scoped Values

Quiz

What does this print?

ScopedValue.where(USER, "John Doe").run(() -> {
    System.out.println(USER.get());
    ScopedValue.where(USER, "Jane Doe").run(() -> System.out.println(USER.get()));
    System.out.println(USER.get());
});
Answer
John Doe
Jane Doe
John Doe

The nested run() only rebinds USER for its own body; once it returns, the outer binding is restored unchanged.

Note on testing: JDK 25 (required by this project's Gradle toolchain) could not be provisioned in this sandbox — the toolchain auto-download host (api.foojay.io) and direct JDK download hosts are blocked by the environment's network policy. The ScopedValue API used here is unchanged from its JDK 21 preview form (just finalized, no --enable-preview needed), so every test method's logic was independently verified by compiling and running an equivalent standalone program with javac --release 21 --enable-preview / java --enable-preview, confirming the exact assertions in this PR. Opening as a draft; CI will run the real build on JDK 25.

Closes #87

@mperor mperor added the daily Queued for the daily learning PR label Oct 23, 2026
@github-actions

Copy link
Copy Markdown

Test Results

266 tests  +4   263 ✅ +4   5s ⏱️ -1s
 78 suites ±0     3 💤 ±0 
 78 files   ±0     0 ❌ ±0 

Results for commit 836dda6. ± Comparison against base commit b209ba5.

This pull request skips 1 and un-skips 1 tests.
pl.mperor.lab.java.design.pattern.creational.singleton.SingletonTest ‑ testSerializationSafe()
pl.mperor.lab.java.design.pattern.creational.singleton.SingletonTest ‑ testThreadSafe()

@mperor
mperor marked this pull request as ready for review October 23, 2026 11:26
@mperor
mperor merged commit 52ef489 into master Oct 23, 2026
2 checks passed
@mperor
mperor deleted the DAILY/87-scoped-values-vs-threadlocal branch October 23, 2026 11:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

daily Queued for the daily learning PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Java 25: Scoped Values vs ThreadLocal (JEP 506)

2 participants