Concurrency

Virtual Threads

Virtual threads (Java 21) are JVM-scheduled threads costing ~1 KB instead of ~1 MB: blocking parks the virtual thread and frees its carrier. Thread-per-task becomes viable at millions of tasks — write blocking code, get event-loop scale.
  • Cheap: create one per task, never pool them
  • Blocking I/O unmounts the virtual thread from its carrier — the OS thread moves on
  • Executors.newVirtualThreadPerTaskExecutor() is the drop-in server pattern
  • They help I/O-bound workloads; CPU-bound work gains nothing
  • Limit resources with a Semaphore, not by pooling threads
  • Caveats: ThreadLocal per-"thread" caches multiply; pinning via synchronized fixed in JDK 24 (JEP 491)
A thousand concurrent calls, straight-line code
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    List<Future<Price>> results = suppliers.stream()
            .map(s -> executor.submit(() -> {
                var response = http.send(requestFor(s), BodyHandlers.ofString());  // blocks — cheaply
                return parsePrice(response.body());
            }))
            .toList();
    // 1,000 suppliers → 1,000 virtual threads → a handful of carrier threads
}

Mechanics: a virtual thread's stack lives on the heap. When it hits a blocking operation the JDK has retrofitted (sockets, locks, sleeps, queues), the continuation unmounts from its carrier (a small FJ pool of OS threads, ~1 per core) and the carrier picks up another virtual thread. Millions of blocked virtual threads cost heap, not OS threads — the C10K problem dissolved into ordinary code.

What changes in design: stop sizing thread pools for I/O concurrency (the pool was the bottleneck), stop contorting into reactive pipelines for scale alone, and bound access to scarce resources explicitly with a Semaphore. What doesn't change: Thread Safety — virtual threads are real threads; every visibility and atomicity rule applies unchanged.

Sources
  • Core Java, Volume I: Fundamentals (13th ed.)Ch. 10 — Concurrency (virtual threads)
  • Optimizing Cloud Native Java (2nd ed.)Ch. 13, 15 — Concurrent Performance; The Future
  • Learning Java (6th ed.)Ch. 9 — Threads