Java 21 virtual threads are scheduled by the JVM, not the OS, so blocking work scales to very large numbers of concurrent tasks with ordinary blocking code.
var counter = new AtomicInteger();
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10_000; i++) {
executor.submit(() -> {
Thread.sleep(Duration.ofMillis(5)); // blocking is fine on a virtual thread
counter.incrementAndGet();
return null;
});
}
} // close() waits for every task to finish
System.out.println("completed tasks = " + counter.get());
completed tasks = 10000
Run this yourself in the Online Java Compiler, spin up a live REST API in the API Sandbox, or practise with Java interview questions.
Published 2026-07-30