Virtual threads (Java 21)

Multithreading · lesson 15 of 38 · 4 min read

Run hundreds of thousands of blocking tasks on a few OS threads, using plain blocking code.

Open this lesson in the learning hub

Key points

  • A virtual thread is scheduled by the JVM, not the OS. Each starts tiny and grows on demand, so a million of them is realistic.
  • When it blocks on I/O it unmounts from its carrier thread, freeing that OS thread to run something else immediately.
  • Write ordinary blocking code. No callbacks and no reactive types, yet it scales. Stack traces and debuggers still make sense.
  • Use Executors.newVirtualThreadPerTaskExecutor(). Never pool virtual threads: one per task is the whole idea.
  • They only help I/O-bound work. CPU-bound work still wants roughly one thread per core.
  • In Java 21, blocking inside synchronized pins the carrier thread, so prefer ReentrantLock there. JDK 24 removed that limitation.

Example

import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        AtomicInteger done = new AtomicInteger();
        Instant start = Instant.now();

        // 10,000 threads that mostly wait. With platform threads this would hurt.
        try (ExecutorService pool = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < 10_000; i++) {
                pool.submit(() -> { Thread.sleep(100); done.incrementAndGet(); return null; });
            }
        }   // close() waits for every task to finish

        System.out.println("tasks done : " + done.get());
        System.out.println("elapsed    : " + Duration.between(start, Instant.now()).toMillis() + " ms");

        Thread v = Thread.ofVirtual().name("v-1").start(() -> System.out.println("hi from a virtual thread"));
        v.join();
        System.out.println("isVirtual  : " + v.isVirtual());
    }
}

Virtual threads make blocking code cheap again.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Multithreading course, and every lesson in it is listed on the Multithreading contents page.