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();
}
}
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
).