Unlock Massive Concurrency: How Java Virtual Threads Transform Backend Performance
This article explains Java 21 virtual threads, their lightweight nature, cost advantages over platform threads, practical code examples, performance experiments, and how they enable high‑throughput backend applications with minimal code changes.
Previously, we introduced Java 21's virtual threads in our Java features column.
Virtual threads are a lightweight concurrency model that dramatically reduces the effort required to build, maintain, and observe high‑throughput applications.
What Is a Virtual Thread?
A virtual thread is a lightweight thread that aims to achieve near‑optimal hardware utilization for each request.
One thread per request style – programs can scale close to the best hardware usage.
In the "one thread per request" model, each HTTP request or SQL transaction runs on its own thread, i.e., request = transaction = thread.
The cost of this model depends on the cost of Java threads. Platform threads are expensive: they require memory for the call stack and about a millisecond to start.
Platform threads store their call stack in memory.
Starting a platform thread costs roughly one millisecond.
Because platform threads are costly, achieving high CPU utilization with them is difficult.
Deep Dive into Coding
To create a virtual thread, use Thread.ofVirtual():
Thread.ofVirtual()
.name("didispace-virtual-thread")
.start(runnable);Tip: To create a platform thread, use Thread.ofPlatform() .
Virtual threads run on top of platform threads. Below is an example that creates ten virtual threads, prints their names, sleeps, and joins them.
var threads = IntStream.range(0, 10)
.mapToObj(i -> Thread.ofVirtual().unstarted(() -> {
System.out.println(Thread.currentThread().getName());
try { Thread.sleep(10); } catch (InterruptedException e) {}
System.out.println(Thread.currentThread().getName());
}))
.toList();
threads.forEach(Thread::start);
threads.forEach(t -> { try { t.join(); } catch (InterruptedException e) {} });When a virtual thread blocks, its stack moves from the platform thread to the heap, freeing the platform thread to run other virtual threads. Unblocking moves the stack back, possibly to a different platform thread. This makes blocking virtual threads much cheaper than blocking platform threads.
All JDK blocking operations—IO, synchronization, and Thread.sleep —have been refactored to take advantage of virtual threads.
How Many Platform Threads Are Needed?
Experiments show that a small number of platform threads can service many virtual threads. For example, 5 virtual threads used 3 platform threads (2 ms), 10 virtual threads still used 3 platform threads (4 ms), 100 virtual threads used 7 platform threads, and even 1 000 000 virtual threads ran with only 8 platform threads in under a second.
Launching Ten Million Virtual Threads
On an old laptop, launching ten million virtual threads completed in under 7 seconds, demonstrating the scalability of this model.
Virtual threads enable Java applications to achieve high concurrency with minimal code changes and dramatically lower resource consumption.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
