Java

Java Virtual Threads: High-Concurrency without the Complexity

Master Project Loom. Learn how Virtual Threads decouple Java threads from OS threads to allow millions of concurrent tasks on standard hardware.

Sachin Sarawgi·April 20, 2026·2 min read
#java#concurrency#project-loom#multithreading#performance

Java Virtual Threads (Project Loom)

Historically, Java used OS-level threads. Each thread cost ~1MB of memory for its stack. If you wanted 10,000 concurrent users, you needed 10GB of RAM just for the threads. Virtual Threads (Java 21) change the physics of Java concurrency.

1. Platform vs. Virtual Threads

  • Platform Threads: 1:1 mapping with OS threads. Expensive to create and switch.
  • Virtual Threads: M:N mapping. Millions of virtual threads are multiplexed over a few "Carrier" (OS) threads.

![Diagram showing many Virtual Threads mapping to few Carrier Threads]

2. The Scheduler and "Mounting"

When a virtual thread performs a blocking I/O call (e.g., a database query or an HTTP request), the JVM unmounts the virtual thread from its carrier thread. The carrier thread is now free to run a different virtual thread. Once the I/O is done, the scheduler remounts the original virtual thread to continue execution.

![Memory stack diagram comparing Platform (fixed large block) vs. Virtual (dynamic small heap-based stack)]

3. Production Insights: The "Pinning" Danger

A virtual thread is pinned to its carrier thread if it executes a blocking operation inside a synchronized block or a native method. When pinned, the carrier thread is blocked, defeating the entire purpose of virtual threads.

Fix: Replace legacy synchronized blocks with ReentrantLock.

// Legacy: Causes Pinning
public synchronized void doWork() {
    blockingCall();
}

// Modern: Allows Unmounting
private final ReentrantLock lock = new ReentrantLock();
public void doWork() {
    lock.lock();
    try {
        blockingCall();
    } finally {
        lock.unlock();
    }
}

4. Connection Pools

With millions of threads, your database connection pool (e.g., HikariCP) becomes the new bottleneck. You can't have 1 million DB connections. You must still size your pools based on the database's capacity, not the thread count.


Next: The Transactional Outbox Pattern: Reliability Previous: Distributed Caching at Scale


Related Guides

📚

Recommended Resources

Java Masterclass — UdemyBest Seller

Comprehensive Java course covering Java 17+, OOP, concurrency, and modern APIs.

View Course
Effective Java, 3rd EditionMust Read

Joshua Bloch's classic guide to writing clear, correct, and efficient Java code.

View on Amazon
Java Concurrency in Practice

The authoritative book on writing thread-safe, concurrent Java programs.

View on Amazon

Practical engineering notes

Get the next backend guide in your inbox

One useful note when a new deep dive is published: system design tradeoffs, Java production lessons, Kafka debugging, database patterns, and AI infrastructure.

No spam. Just practical notes you can use at work.

Sachin Sarawgi

Written by

Sachin Sarawgi

Engineering Manager and backend engineer with 10+ years building distributed systems across fintech, enterprise SaaS, and startups. CodeSprintPro is where I write practical guides on system design, Java, Kafka, databases, AI infrastructure, and production reliability.

Found this useful? Share it: