Java 21 Virtual Threads vs FixedThreadPool: Performance Comparison with 10,000 Tasks

In Java 21, Virtual Threads have become a GA (General Availability) feature. The main goal is to make each concurrent task as cheap as creating an object, avoiding the complexity and memory overhead of traditional thread pools.

Traditional FixedThreadPool has some drawbacks compared to virtual thread pool.

  1. The number of threads is fixed and cannot grow for each task.
  2. With many blocking tasks, tasks must queue up, significantly increasing total execution time.
  3. Each OS thread consumes a large native stack.

Virtual Threads are completely different.

  1. Creation and scheduling cost is almost the same as creating an object.
  2. Every task can easily get its own thread, without being limited by pool size.
  3. When blocked, they are suspended without occupying a carrier OS thread.

1. Experiment Design

To illustrate the difference, we designed a simple benchmark.

Experiment Code

runWithExecutor(Executors.newFixedThreadPool(200), taskCount, "FixedThreadPool");
runWithExecutor(Executors.newVirtualThreadPerTaskExecutor(), taskCount, "VirtualThread");
private static void printMemoryUsage(String label) {
    Runtime runtime = Runtime.getRuntime();
    long used = (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024);
    System.out.printf("[%s] Used Memory: %d MB%n", label, used);
}

Experiment Results

Virtual Threads vs FixedThreadPool Experiment Results

Comparison Table

Executor Time (ms) Memory Increase (MB)
FixedThreadPool (200) 5194 90
VirtualThread (10,000) 208 20

2. Virtual Threads Conclusion

Execution Time

Virtual Threads improved throughput by more than 25x.

Virtual Threads vs FixedThreadPool Execution Time Comparison

Memory Usage

Virtual Threads consumed significantly less memory even with 10,000 tasks.

Virtual Threads vs FixedThreadPool Memory Usage Increase Comparison

This experiment demonstrates the lightweight and high-concurrency advantage of Java 21 Virtual Threads: enabling “one thread per task” without the heavy cost of traditional threads.

Significant performance advantage

Lower memory

Core design

Best use cases

Not suitable for