Repository navigation
Java 24: Virtual threads no longer pinned by synchronized (JEP 491) - #121
Merged
Merged
Conversation
Test Results306 tests +3 303 ✅ +3 5s ⏱️ -1s Results for commit a8298de. ± Comparison against base commit a9c068a. This pull request skips 1 and un-skips 1 tests. |
mperor
marked this pull request as ready for review
November 5, 2026 07:14
|
@mperor ⏰ This daily topic is still waiting for your review. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What you'll learn
synchronizedused to exhaust carrier threads, and how JEP 491 fixes that in JDK 24+synchronizedblock ignoresinterrupt(), unlikeReentrantLock#lockInterruptiblyWhy it matters
Picking
synchronizedvs.java.util.concurrentlocks for virtual-thread code used to be a real capacity-planning decision; JEP 491 removes most of that cost, but interruptibility,tryLock, fairness and multipleConditions are still reasons to reach forReentrantLock.Note: this sandbox's network policy blocks every JDK 24/25 distribution host, so
./gradlewcould not run locally. I validated the new tests by compiling and running them standalone against a local JDK 21 + JUnit 6.1: they compile cleanly, andtestManyVirtualThreadsBlockingInsideSynchronizedDoNotPinCarriersfails on JDK 21 exactly as predicted (elapsed time matches the pinned-carrier estimate almost exactly, confirming real pinning). CI has since run on JDK 25 and is green (306 tests, +3, 0 failures), confirming the fix actually works there.Read more
JEP 491: Synchronize Virtual Threads without Pinning
Quiz
Before JDK 24, 1000 virtual threads each enter their own (uncontended)
synchronizedblock and callThread.sleep(80)inside it, on a machine with 8 carrier threads. Roughly how long does the whole batch take, and why?Answer
Roughly
1000 / 8 * 80 ms≈ 10 seconds. Blocking insidesynchronizedpinned each virtual thread to its carrier thread, so only 8 could be "sleeping" at once; the rest queued up waiting for a free carrier. On JDK 24+ (JEP 491) the carrier is released instead, so all 1000 run close to concurrently and the batch finishes in about 80 ms.Closes #97