The Platform Thread Problem
Java's traditional threading model maps one application thread to one OS thread. This works until you hit I/O.
A thread waiting on a database query or API call sits idle, consuming 1-2MB of stack memory and an OS scheduling slot. Scale to thousands of concurrent requests and you hit limits - hence thread pools, reactive frameworks, and callback complexity.
The question Project Loom answered: if a thread isn't computing, why treat it as a scarce resource?
How Virtual Threads Work
Virtual threads are JVM-managed, using a few KB each. Millions can run on a handful of "carrier" platform threads.
When a virtual thread hits blocking I/O, the JVM parks it and moves the carrier thread to other work. No OS thread sits idle. When I/O completes, any available carrier can resume the virtual thread.
Benchmarks show the difference. Handling 10,000 concurrent tasks uses 7.8MB with virtual threads versus 19.2GB with platform threads. Execution time drops from 346ms to 84ms.
Spring Boot 3.2 added configuration support. Creating them is straightforward:
Thread thread = Thread.startVirtualThread(() -> {
doWork();
});
The Pinning Problem
Virtual threads don't magically fix everything. Two scenarios cause "pinning" - where a virtual thread blocks its carrier thread like an old-school platform thread:
- Synchronized blocks: The JVM can't unmount a virtual thread holding a monitor lock. Use
ReentrantLockinstead. - Native calls: JNI operations pin the carrier until they complete.
Database connection pools can amplify this. If connections are managed with synchronized blocks, high concurrency creates carrier thread congestion.
Java 24 added better pinning detection. Production monitoring matters - threads spending significant time pinned defeat the purpose.
When to Use Each
Use virtual threads for:
- Web servers and APIs
- Database queries
- External service calls
- Anything spending time waiting
Use platform threads for:
- CPU-intensive computation
- Tight OS integration
- Code with unavoidable synchronized blocks
The rule: virtual threads optimize waiting, platform threads optimize computing.
What This Means
Virtual threads don't make Java faster - they stop penalizing simple blocking code. Applications can handle massive concurrency without async complexity or thread pool tuning.
The trade-off: teams need to audit synchronized blocks and understand pinning. For I/O-heavy workloads, the return is worth it. For CPU-bound services, platform threads remain the right choice.
Project Loom stabilized in Java 21. Enterprise adoption is ramping through Spring Boot and other frameworks. The model works - as long as you know when carrier thread pinning will bite you.