DEV Community

Said Olano
Said Olano

Posted on

Understanding Java Virtual Threads: A Practical Guide

Understanding Java Virtual Threads: A Practical Guide

Java's Project Loom introduced virtual threads as a stable feature in Java 21, fundamentally changing how we approach concurrent programming on the JVM. In this post, we'll explore what virtual threads are, why they matter, and how to use them effectively.

What Are Virtual Threads?

Traditional Java threads (platform threads) are thin wrappers around operating system threads. Each OS thread consumes significant memory (typically ~1MB of stack space) and involves costly context switches. This limits applications to a few thousand concurrent threads before performance degrades.

Virtual threads are lightweight threads managed by the JVM rather than the OS. Thousands—even millions—can run concurrently because they're mounted onto a small pool of carrier (platform) threads only when actively executing.

Creating Virtual Threads

The simplest way to start a virtual thread:

java
Thread.startVirtualThread(() -> {
System.out.println("Running in a virtual thread: " + Thread.currentThread());
});

For structured concurrency with an executor:

java
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
return i;
});
});
}

This code spins up 10,000 concurrent tasks without exhausting system resources.

How They Work

When a virtual thread hits a blocking operation (like I/O), the JVM unmounts it from its carrier thread, freeing that carrier to run other virtual threads. Once the blocking operation completes, the virtual thread is remounted. This happens transparently—your code looks like ordinary blocking code.

java
// This blocking call no longer wastes an OS thread
String response = httpClient.send(request, BodyHandlers.ofString()).body();

Best Practices

  1. Don't pool virtual threads. They're cheap to create; use one per task.
  2. Avoid synchronized blocks around blocking calls, as they can pin the carrier thread. Prefer ReentrantLock instead.
  3. Use them for I/O-bound work, not CPU-intensive tasks where platform threads suffice.

Virtual Threads in Spring Boot

Spring Boot 3.2+ supports virtual threads with a single property:

properties
spring.threads.virtual.enabled=true

This makes Tomcat handle each request on a virtual thread, dramatically improving throughput for I/O-heavy web applications.

Conclusion

Virtual threads let you write straightforward, blocking-style code that scales to massive concurrency. By removing the one-thread-per-OS-thread constraint, Java modernizes server-side development without forcing developers into complex reactive paradigms. If you're on Java 21 or later, they're worth adopting today.

Top comments (0)