Create one virtual thread per independent blocking task and wait for every submitted result or failure.
Build a Complete Mental Model
Concurrency requires deliberate handling of shared mutable state, ordering, cancellation, and failure propagation. Virtual threads, finalized in Java 21, scale blocking-I/O workloads; they do not make CPU work faster and should not be pooled.
Apply It in Real Java
Run independent blocking-style tasks with a virtual-thread-per-task executor, join every result or failure, and close the executor lifecycle.
After This Lesson
import java.util.concurrent.Executors;
class Main {
public static void main(String[] args) throws Exception {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var result = executor.submit(
() -> "Virtual: " + Thread.currentThread().isVirtual()
);
System.out.println(result.get());
}
}
}Virtual: trueTry It Yourself
Run independent blocking-style tasks with a virtual-thread-per-task executor, join every result or failure, and close the executor lifecycle.
Common Mistake
Assuming a thread-safe collection alone fixes every data race or pooling virtual threads as though they were scarce platform threads.
JEP 444: Virtual Threads — OpenJDK