Java Virtual Threads: What They Change

Virtual threads in Java 21 make high-throughput concurrent applications simpler without reactive frameworks.

Key Insights

  • Virtual threads are lightweight and managed by the JVM, not the OS
  • They eliminate the need for reactive frameworks in most I/O-bound applications
  • Existing synchronized code may need refactoring to avoid pinning virtual threads

The Problem They Solve

Traditional Java threads map 1:1 to OS threads. This limits concurrency to thousands of threads at most. Virtual threads remove that constraint:

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

When to Use Them

Virtual threads excel at I/O-bound workloads: HTTP servers, database queries, file operations. They don’t help CPU-bound tasks since those still need real CPU cores.

Migration Considerations

Replace thread pools with virtual thread executors. Watch for synchronized blocks that perform I/O — these pin the carrier thread. Use ReentrantLock instead.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.