Virtual threads (Java 21, Project Loom) are lightweight threads managed by the JVM rather than the operating system. You can create millions of them, and when one blocks on I/O, the JVM parks it and frees the underlying OS thread for other work. This keeps the simple thread-per-request style while scaling like asynchronous code.
Key Points: • Platform threads map one-to-one to OS threads. Each reserves about 1 MB of stack, so thousands of them are expensive. • Virtual threads run on a small pool of carrier (platform) threads. Blocking calls such as JDBC, HTTP or Thread.sleep() unmount them instead of blocking the carrier. • They help I/O-bound workloads (web requests, database calls), not CPU-bound work. • Don't pool virtual threads. Create one per task, for example with Executors.newVirtualThreadPerTaskExecutor(). • In Spring Boot 3.2+, setting spring.threads.virtual.enabled=true makes Tomcat and @Async use virtual threads.
Code Example:
import java.time.Duration;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
public class VirtualThreadsDemo {
public static void main(String[] args) {
AtomicInteger done = new AtomicInteger();
long start = System.currentTimeMillis();
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000).forEach(i -> executor.submit(() -> {
Thread.sleep(Duration.ofMillis(500)); // simulated I/O call
done.incrementAndGet();
return i;
}));
} // close() waits for all tasks
long seconds = Math.round((System.currentTimeMillis() - start) / 1000.0);
System.out.println("Tasks completed: " + done.get());
System.out.println("Took about " + seconds + " second(s), not 10,000 x 0.5 seconds");
System.out.println("Is virtual: " + Thread.ofVirtual().unstarted(() -> {}).isVirtual());
}
}Output:
Tasks completed: 10000 Took about 1 second(s), not 10,000 x 0.5 seconds Is virtual: true
Good to Know: Make the point that virtual threads make blocking cheap, not fast. Each request still takes as long as its database call; the server can just handle far more of them at the same time. Then mention when not to use them, which is the natural follow-up question.
Interview Tip: A concise interview answer is:
"Virtual threads are lightweight threads managed by the JVM instead of the operating system, so I can run millions of them. When one blocks on I/O, it's unmounted from its carrier thread, which lets simple blocking code scale like asynchronous code. They help I/O-bound work, not CPU-bound work."
Previous question: What are helpful NullPointerExceptions in Java 14+?
Next question: When should you not use virtual threads, and what is thread pinning?