principlejavaspringModerate
Virtual threads (Java 21): do not pool them, let the scheduler manage them
Viewed 0 times
Java 21, Spring Boot 3.2+
virtual threadsJava 21LoomnewVirtualThreadPerTaskExecutorthread pinningSpring Boot 3.2
Problem
Developers apply the platform thread mental model to virtual threads and create thread pools for them. Virtual threads are cheap to create — pooling them wastes the benefit and can introduce deadlocks when a pool exhausts its limit while all threads are blocked on I/O.
Solution
Create virtual threads per task and let the JVM scheduler park them during I/O:
Avoid synchronized blocks in virtual-thread-heavy code — they pin the virtual thread to the carrier platform thread, blocking it from running other virtual threads.
// BAD — pooling virtual threads defeats their purpose
ExecutorService pool = Executors.newFixedThreadPool(100, Thread.ofVirtual().factory());
// GOOD — unbounded virtual thread executor
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
// Spring Boot 3.2+ — enable virtual threads for all request handling
// application.yml
// spring.threads.virtual.enabled: true
// Manual task submission
try (var exec = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<Result>> futures = tasks.stream()
.map(task -> exec.submit(() -> process(task)))
.toList();
// collect results
}Avoid synchronized blocks in virtual-thread-heavy code — they pin the virtual thread to the carrier platform thread, blocking it from running other virtual threads.
Why
Virtual threads are implemented by the JVM as continuations on a small pool of carrier platform threads. When a virtual thread blocks on I/O, the JVM unmounts it and mounts another. Pooling limits the number of concurrent virtual threads and reintroduces the scarcity problem that virtual threads solve.
Gotchas
- synchronized blocks pin virtual threads to carrier threads — prefer ReentrantLock for critical sections in high-concurrency code
- Virtual threads have no performance benefit for CPU-bound work — only I/O-bound tasks benefit from the parking model
- Spring Boot 3.2+ enables virtual threads with a single property — test thoroughly as some blocking libraries may behave differently
Revisions (0)
No revisions yet.