Lesson 8 / 38

Executors & Futures

Decouple task submission from thread management with ExecutorService, run Callables, collect results from Future, and always shut the pool down.

Submit work to a pool

An ExecutorService decouples task submission from thread management. submit(Callable<T>) returns a Future<T>; future.get() blocks for the result. Always shutdown() the pool.

try (var pool = Executors.newFixedThreadPool(4)) {
    List<Future<Integer>> fs = pool.invokeAll(tasks);
    for (Future<Integer> f : fs) total += f.get();
}   // close() shuts down and waits (Java 19+)

Pick the right pool

newFixedThreadPool(n) for bounded CPU-bound work, newCachedThreadPool() for many short-lived bursty tasks, and Java 21's Executors.newVirtualThreadPerTaskExecutor() for thousands of blocking IO tasks — one virtual thread per task, no pool sizing needed.

try (var pool = Executors.newVirtualThreadPerTaskExecutor()) {
    urls.forEach(url -> pool.submit(() -> fetch(url)));
}