ExecutorService.invokeAll submits every task and blocks until all are done, which gives a simple fork-join shape over virtual threads.
List<Callable<Integer>> tasks = IntStream.rangeClosed(1, 5)
.mapToObj(n -> (Callable<Integer>) () -> {
Thread.sleep(Duration.ofMillis(10));
return n * n;
})
.toList();
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<Integer>> futures = executor.invokeAll(tasks);
int total = 0;
for (Future<Integer> f : futures) total += f.get();
System.out.println("squares of 1..5 sum to " + total);
}
squares of 1..5 sum to 55
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