Explain invokeAll and invokeAny on ExecutorService: their blocking and return semantics, how they handle exceptions and cancellation, and when you'd reach for each.
answer
- invokeAll = run all, block till all done, ordered List<Future>
- invokeAny = block till first SUCCESS, return raw result, cancel rest
- invokeAll failures are per-Future (get() throws); method itself doesn't throw
- invokeAny throws ExecutionException only if ALL fail
- Timeout: invokeAll cancels stragglers; invokeAny throws TimeoutException
- invokeAll = scatter/gather; invokeAny = race/redundancy
basics
~20 sBoth take a collection of Callables. invokeAll runs them all and blocks until every one finishes, returning a list of Futures in the same order. invokeAny runs them and blocks until the first one succeeds, returning that single result and cancelling the rest. Use invokeAll for fan-out/gather; invokeAny when any one good answer suffices.
solid answer
~50 sinvokeAll(tasks) submits a whole collection of Callables and blocks until all complete (or the optional timeout fires), returning a List<Future> in the same order as the input — so result N pairs with task N. Each Future is already done; calling get() returns its value or throws ExecutionException if that task failed. So invokeAll never throws a task's exception directly — failures are per-Future. invokeAny(tasks) blocks until the first task completes successfully, returns that result directly (the raw value, not a Future), and cancels all the others. If every task fails, it throws ExecutionException. invokeAll is the fan-out/gather primitive — split work, wait for all, collect ordered results. invokeAny is for redundancy or racing — query several replicas/sources and take whichever answers first, e.g. hitting three mirrors and using the fastest. Both have timeout overloads; on timeout invokeAll cancels unfinished tasks (their Futures report cancelled) and invokeAny throws TimeoutException.
go deeper
Knows invokeAll waits for all tasks and invokeAny returns the first one's result, both taking a list of tasks.
Explains invokeAll returns ordered Futures while invokeAny returns a single value and cancels the rest, and that both block.
Details per-Future failure isolation in invokeAll, invokeAny's first-success/cancel-losers semantics and all-fail behavior, timeout handling, and picks each by use case (scatter/gather vs racing).
Designs redundancy/hedging and fan-out aggregation strategies with these primitives, addressing correctness-vs-completion in invokeAny, resource cost of cancelled losers, and when to prefer CompletableFuture/structured concurrency instead.
## The batch-submission methods Beyond submitting one task at a time, `ExecutorService` offers two methods that take an entire **collection of Callables** and coordinate them. Both are **blocking** — they don't return until a defined condition is met — which makes them convenient higher-level primitives over raw `submit` + manual `Future` juggling. Recall a **Callable<T>** is a task returning `T` (and may throw); a **Future<T>** is a handle to a pending result. ### invokeAll — run everything, wait for all ```java <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException; <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException; ``` - **Blocks** until **all** tasks finish (normally or by throwing), or the timeout elapses. - **Returns** a `List<Future<T>>` whose **order matches the input collection's iterator order** — element *i* of the result is the Future for the *i*-th task. This positional correspondence is the whole reason it's ergonomic. - Every returned Future is already **done**. To read results you iterate and call `get()` on each: a successful task's Future returns its value; a **failed task's** Future throws `ExecutionException` (cause = the original exception) from `get()`. So **invokeAll itself does not throw a task's exception** — failures are isolated *per Future*. One task failing does not stop the others. - **On timeout:** any tasks not finished by the deadline are **cancelled**; their Futures report `isCancelled() == true`, and `get()` throws `CancellationException`. This is the classic **scatter/gather** (fan-out/fan-in) primitive: split a job into N independent pieces, run them in parallel, wait, then collect ordered results. ### invokeAny — race, take the first success ```java <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException; <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; ``` - **Blocks** until **one** task completes **successfully**. - **Returns** that task's **result directly** — the raw `T`, *not* a Future. You don't get to know which task won. - As soon as one succeeds, the method **cancels all the other** still-running tasks (interrupting them) — they're no longer needed. - **If every task fails**, it throws `ExecutionException` (wrapping one of the failures). Tasks that fail are simply skipped while it waits for any other to succeed. - **On timeout:** throws `TimeoutException` if no task succeeded in time. This is the **racing/redundancy** primitive: when *any one* correct answer suffices and you want the fastest. Examples: query three database replicas and use whichever responds first; resolve a value from several caches/mirrors; hedge a slow request. ### Choosing between them | Need | Use | |---|---| | All results, in order, every task must run | `invokeAll` | | Just one result, fastest wins, cancel the rest | `invokeAny` | | Tolerate partial failure, inspect each outcome | `invokeAll` (per-Future) | | Redundant sources, want lowest latency | `invokeAny` | ### Interruption and the InterruptedException Both declare `throws InterruptedException` because they block the *calling* thread; if that thread is interrupted while waiting, they abort and (for invokeAll) cancel unfinished tasks. Always restore the interrupt status (`Thread.currentThread().interrupt()`) when you catch it. ### Worked example ```java List<Callable<Integer>> tasks = List.of( () -> slowLookup("a"), () -> slowLookup("b"), () -> slowLookup("c")); // invokeAll: ordered results, partial-failure tolerant List<Future<Integer>> futures = es.invokeAll(tasks); for (Future<Integer> f : futures) { try { use(f.get()); } catch (ExecutionException e) { log(e.getCause()); } // this task failed; others still fine } // invokeAny: first success wins, others cancelled Integer first = es.invokeAny(tasks); // throws ExecutionException only if ALL fail ``` ### Why these over manual submit loops You *could* call `submit` N times and loop over the Futures yourself. invokeAll/invokeAny package the common coordination (waiting, ordering, cancel-the-losers, timeout handling) into one call, reducing boilerplate and bugs — particularly the easy-to-get-wrong cancellation of losing tasks in the invokeAny case.
- In invokeAll, if the third task throws but the first two succeed, what does the returned list look like?It still has three Futures in order. Futures 0 and 1 return their values from get(); Future 2's get() throws ExecutionException wrapping the original exception. invokeAll itself does not throw — failure is isolated to that one Future.
- You hit three replicas with invokeAny and the fastest returns a wrong/garbage value but doesn't throw. What's the catch?invokeAny returns the first task that completes WITHOUT throwing — it has no notion of 'correct'. A successful-but-wrong result wins the race. If correctness must be validated, the Callable itself should throw on invalid results so invokeAny treats it as a failure and waits for another.
saying these in an interview costs you the question
- Saying invokeAll throws when a task fails — it doesn't; each failure is isolated in that task's Future.
- Thinking invokeAny returns a Future — it returns the raw result value directly.
- Believing invokeAll's result order is arbitrary — it matches the input collection's order.
- Claiming invokeAny waits for the first task to FINISH (even failing) — it waits for the first to SUCCEED.
- Forgetting invokeAny cancels the losing tasks once one succeeds.