Executors and Futures¶
An Executor decouples task submission from execution policy. An
ExecutorService adds lifecycle and result management.
Select and own the policy¶
- fixed pools bound active platform threads for known CPU or blocking policies;
- work-stealing pools support recursively decomposed compute work;
- virtual-thread-per-task executors support high-concurrency blocking style;
- scheduled executors handle delayed and periodic work.
Every created executor needs an owner and shutdown policy. Queue capacity, rejection behavior, task duration, and downstream capacity are part of system design. See pools and bounded resources for saturation, connection leases, capacity measurement, and nested-pool risks.
Future and CompletableFuture¶
A Future represents one eventual result and supports waiting and cancellation.
CompletableFuture builds asynchronous dependency graphs.
CompletableFuture<Integer> total = CompletableFuture
.supplyAsync(() -> loadLeft(), executor)
.thenCombine(
CompletableFuture.supplyAsync(() -> loadRight(), executor),
Integer::sum)
.orTimeout(2, TimeUnit.SECONDS);
Choose synchronous (thenApply) versus asynchronous (thenApplyAsync)
continuations deliberately and pass an executor when execution policy matters.
Exceptions are part of the graph; define where they are translated or recovered.
Backpressure¶
Submitting work faster than it completes creates an implicit or explicit queue. An unbounded queue trades immediate rejection for unbounded latency and memory risk. Bound concurrency at the resource that cannot scale, and propagate deadlines or cancellation rather than allowing obsolete work to accumulate. A queue does not provide backpressure unless reaching its bound constrains the producer under an explicit policy; the resilience-controls guide develops that distinction.