Virtual Thread Pool in JDK 19

March 12, 2025

Virtual Thread Pool in JDK 19

JDK 19 introduced the Executors.newVirtualThreadPerTaskExecutor() method, which provides an unbounded thread pool where each task runs in its own virtual thread.

Example: Using Virtual Threads in a Thread Pool

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class VirtualThreadPoolExample {
    public static void main(String[] args) {
        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < 10; i++) {
                int taskNumber = i;
                executor.submit(() -> {
                    System.out.println("Task " + taskNumber + " is running on thread: " + Thread.currentThread());
                    try {
                        Thread.sleep(1000); // Simulating work
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                });
            }
        } // The try-with-resources block ensures the executor is properly closed.
    }
}

Key Points:

  • Executors.newVirtualThreadPerTaskExecutor() creates an unbounded thread pool.
  • Each task gets its own virtual thread, avoiding OS thread limitations.
  • The try-with-resources ensures that the executor shuts down properly.
  • Unlike traditional ThreadPoolExecutor, virtual threads are not limited by core count.

When to Use Virtual Threads?

  • High-concurrency applications like web servers or I/O-bound tasks.
  • Reducing memory overhead caused by platform threads.
  • Simplifying asynchronous programming without callbacks or CompletableFuture.

Java Platform Thread Pool (Traditional Thread Pool)

In Java, platform threads (also called OS threads) are managed by the operating system and backed by the underlying kernel threads. Unlike virtual threads (introduced in JDK 19), platform threads are more heavyweight but are necessary for CPU-bound tasks.

Example: Using Fixed Thread Pool (Recommended for CPU-Bound Tasks as process images, ...)

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class PlatformThreadPoolExample {
    public static void main(String[] args) {
        int numThreads = Runtime.getRuntime().availableProcessors(); // Optimal for CPU-bound tasks
        ExecutorService executor = Executors.newFixedThreadPool(numThreads);

        for (int i = 0; i < 10; i++) {
            int taskNumber = i;
            executor.submit(() -> {
                System.out.println("Task " + taskNumber + " running on " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000); // Simulating some work
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        executor.shutdown();
    }
}

Compare Virtual vs Platform Thread

If your task is I/O-bound, consider virtual threads (newVirtualThreadPerTaskExecutor() in JDK 19+) or a cached thread pool (newCachedThreadPool).

If your task is CPU-bound, use a fixed thread pool (newFixedThreadPool).