Virtual Threads & synchronized Pinning (JEP 491) — Interview Questions
⚡ Short Answer
This behavior is genuinely version-dependent, and stating it accurately matters: in Java 21 through 23, a virtual thread that blocked while holding a monitor acquired via synchronized could not unmount from its carrier platform thread — it 'pinned' the carrier for the duration, so blocking I/O inside a synchronized block or method effectively lost virtual threads' scalability benefit for that stretch of code, and the JDK-recommended workaround was to replace hot synchronized blocks with java.util.concurrent.locks.ReentrantLock. JEP 491, delivered in Java 24, reimplemented monitor support so that synchronized no longer pins the carrier thread in the common case — meaning on Java 24+, this specific problem is largely resolved and the ReentrantLock workaround is no longer necessary purely for pinning reasons. Always confirm which JDK a codebase targets before giving blanket advice here.
☕Coffee Chat Question
Concept Made Simple
“How does synchronized interact with virtual-thread carrier pinning, and how did that change in Java 24?”
🧠Mind Map Answer
Remember It Faster
Virtual threads work by unmounting from their carrier platform thread whenever they block, freeing the carrier to run other virtual threads. Before JEP 491, the JVM's monitor implementation (backing synchronized) couldn't safely unmount a virtual thread mid-block — so the carrier stayed pinned, unable to serve anyone else, for as long as that blocking call lasted.
Key takeaway: this is exactly the kind of Java-version nuance that separates a candidate who read one blog post from one who tracks the JDK release notes — the correct answer depends on which JDK the codebase actually runs, not on a single blanket rule.
⌨️Hands-on Keyboard
Learn by Doing
// On Java 21-23: this pins the carrier thread for the duration of blockingCall()
synchronized (lock) {
blockingCall(); // carrier thread can't serve other virtual threads meanwhile
}
// 21-23 workaround: swap to ReentrantLock, which doesn't pin
private final ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
blockingCall(); // carrier CAN unmount and serve other virtual threads
} finally {
lock.unlock();
}
// On Java 24+ (JEP 491): the original synchronized version no longer pins
// in the common case — verify against your actual target JDK before relying on this.🔥What If?
Think Beyond the Expected
If a team is still on Java 21 LTS, is the ReentrantLock workaround still relevant advice?
Yes — JEP 491 shipped in Java 24, so any codebase pinned to Java 21, 22, or 23 (a very common position for teams on the current LTS) still has the pinning behavior and should still prefer ReentrantLock over synchronized in code that virtual threads execute and that blocks. The fix only applies once a team actually upgrades to 24 or later.
😂Real World
This is a real migration-planning question, not trivia: a team adopting virtual threads on Java 21 has to audit synchronized usage (including inherited from legacy libraries they don't control) in any code path virtual threads execute, using -Djdk.tracePinnedThreads to find pinning hotspots — and that audit's urgency changes materially once (and if) the team can move to Java 24+.
🗣️Real Talk from Guru
I'd answer this by anchoring on the JDK version first: 'depends whether you're on 21-23 or 24+' — then explain the mechanism. Giving a single unversioned answer here is exactly the kind of outdated-information mistake that makes an interviewer question whether the rest of my Java 21+ knowledge is current.
🎯Interviewer's Expectation
Keywords they're listening for:
⚠️Common Mistakes
- ✗Stating synchronized 'always pins virtual threads' without a version caveat
- ✗Not knowing JEP 491 (Java 24) changed this behavior at all
- ✗Assuming every team is already on the latest JDK
✅Best Practices
- ✓Anchor pinning advice to the target JDK version explicitly
- ✓Use -Djdk.tracePinnedThreads or JFR to find pinning in an existing codebase
- ✓Prefer ReentrantLock over synchronized in virtual-thread hot paths only while on Java 21–23
🔁Follow-up Questions
- 1What other operations besides synchronized can still pin a virtual thread's carrier?
- 2How would you audit a large legacy codebase for pinning-prone synchronized usage before adopting virtual threads on Java 21?
- 3Why couldn't the JVM safely unmount a virtual thread mid-monitor before JEP 491?
🧩Related Technologies
Continue Learning with AI
Take this question deeper with your favourite AI assistant. Pick a depth, copy the prompt, or open it directly — AI is your learning companion, not a shortcut.
Plain-language foundations
I'm preparing for a software engineering interview and want to understand this from scratch, as a beginner. Topic: Threads & Pools (Multithreading) Interview question: "How does synchronized interact with virtual-thread carrier pinning, and how did that change in Java 24?" Please: 1. Explain the core idea in simple, plain language, using an everyday analogy. 2. Define any technical terms you use. 3. Walk through one small, concrete example. 4. Finish with a single sentence I can easily remember. Keep the tone friendly and assume I'm new to this topic.
Was this answer helpful?
⭐ Featured Products
Support our platform by exploring our recommended products.
As an Amazon affiliate, purchases through these links may earn us a small commission — at no extra cost to you. It helps keep Full Stack Interview Guru free.