Virtual Threads

Virtual Threads on JDK 25 Shift the Bottleneck to Your Code

JDK 24 didn't just tweak virtual threads; it moved the bottleneck.

3 min readInfoQ
Virtual Threads on JDK 25 Shift the Bottleneck to Your Code

The JDK 24 update that removed monitor-related carrier-thread pinning solves a problem most teams didn't know they had. If you adopted virtual threads on Java 21 and hit stalled workloads, you likely blamed the platform. Sandeep Bharadwaj's article makes clear the bottleneck didn't disappear; it moved downstream. That's a harder truth to act on because it shifts responsibility from the JVM to your application code. The practical sequence he outlines, backed by a public benchmark, turns an abstract performance topic into something you can test today.

For readers who have been evaluating AI-driven approaches to concurrency or data workflows, this story connects to a broader pattern: scaling is rarely about the compute layer. Our recent look at Scale Sandboxes Instantly: A New Approach to Concurrent AI Workloads made the same point from a different angle. Modal's engineers didn't just add more threads; they rebuilt the infrastructure around resource limits. The same logic applies here. Virtual threads remove a scheduling constraint, but your database connections, HTTP client pools, and file descriptors still have hard ceilings. If you don't bound those explicitly, you trade one failure mode for another. That's not a regression; it's an evolution in how you reason about capacity.

What we find most valuable in Bharadwaj's piece is the shift in mindset it demands. On Java 21, the pinning issue was binary: your carrier thread was blocked or it wasn't. On JDK 25 LTS, the failure becomes a saturation curve. You can't just upgrade and hope. You need to instrument downstream resources, set explicit semaphores, and load test with realistic backpressure. That's more work upfront, but it's also more honest engineering. The same way Uncover Retrieval Weaknesses: Test Your RAG Pipeline Now argues for adversarial testing over feel-good evaluation sets, this pushes you to stress your assumptions about what's actually bottlenecking your system. You can't fix what you haven't measured, and the benchmark gives you a starting point.

Here's the takeaway we'd quote back to anyone asking whether to upgrade: virtual threads didn't get easier; they got more demanding. The JDK did its part by removing the pinning trap. Now your job is to introduce explicit bounding where the JVM used to hide the problem. If you're moving to JDK 25 LTS, budget time for load testing with real downstream services, not mocked clients. Watch for connection pool exhaustion and thread starvation in your own code, not just the platform. The next production incident won't be a JVM bug; it'll be a design gap you can close now. Start with the benchmark Bharadwaj provides, and make your own failure modes visible before your users do.

From InfoQ

JDK 24 removed the monitor-related carrier-thread pinning that stalled Netflix and similar teams on Java 21. What has replaced it on JDK 25 LTS is downstream-resource saturation: The bottleneck moved and now demands explicit bounding in application code. This article maps the failure modes that surface after virtual-thread adoption and gives a practical sequence backed by a public benchmark.

Read the original at InfoQ