Understanding Java Virtual Threads: A Practical Guide
Java 21 introduced one of the most significant concurrency improvements in the platform's history: virtual threads (Project Loom). In this post, we'll explore what they are, why they matter, and how to use them effectively.
What Are Virtual Threads?
Traditional Java threads (platform threads) map directly to operating system threads. They are relatively expensive—each consumes around 1MB of stack memory, and the OS limits how many you can create. This forced developers into complex asynchronous programming models to achieve high concurrency.
Virtual threads are lightweight threads managed by the JVM rather than the OS. You can create millions of them without exhausting system resources.
java
// Creating a virtual thread
Thread.startVirtualThread(() -> {
System.out.println("Running in a virtual thread");
});
The Problem They Solve
Consider a typical blocking I/O operation:
java
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
return i;
});
});
}
With platform threads, spawning 10,000 tasks would be catastrophic. With virtual threads, the JVM parks the virtual thread when it blocks and reuses the underlying carrier thread for other work.
Key Benefits
- Simplicity: Write straightforward blocking code instead of callback chains or reactive pipelines.
- Scalability: Handle massive concurrency with minimal memory overhead.
-
Compatibility: Existing
ThreadandExecutorServiceAPIs work seamlessly.
Virtual Threads in Spring Boot
Spring Boot 3.2+ offers first-class support. Enable them with a single property:
properties
spring.threads.virtual.enabled=true
This configures Tomcat (or Jetty) to handle each request on a virtual thread, dramatically improving throughput for I/O-bound applications.
Best Practices
- Don't pool virtual threads. Create a new one per task—they're cheap.
-
Avoid
synchronizedblocks around I/O. They can pin the carrier thread. PreferReentrantLock. - Use them for I/O-bound work, not CPU-bound tasks where platform threads still shine.
Conclusion
Virtual threads let you write simple, sequential code that scales to enormous concurrency levels. If you're building I/O-heavy services, adopting Java 21 with virtual threads—especially alongside Spring Boot—can simplify your codebase while boosting performance.
Top comments (0)