The ExecutorService.invokeAll() method submits a collection of Callable tasks, waits until every task has finished, and returns a list of Future objects in the same order as the tasks. Its sibling invokeAny() runs the same kind of collection but returns the result of the first task that finishes successfully and cancels the rest.
We use invokeAll() when a page or a report needs several independent results at once, such as the widgets of a dashboard. We use invokeAny() when several sources can answer the same question and the fastest correct answer is enough, such as identical mirror servers.
The following example loads three dashboard widgets in parallel and reads the results after invokeAll() returns.
List<Callable<String>> widgets = List.of(() -> "weather", () -> "orders", () -> "news");
List<Future<String>> futures;
try (ExecutorService pool = Executors.newFixedThreadPool(3)) {
futures = pool.invokeAll(widgets); // blocks until all 3 are done
}
boolean allDone = futures.stream().allMatch(Future::isDone); // true
String first = futures.get(0).resultNow(); // "weather"
int count = futures.size(); // 3
Notice that we read the results after the try block, because every Future in the list is already complete. We cover the order of the results, failed tasks, the timeout version, invokeAny() in detail, and how both methods compare with submit() and CompletableFuture.
1. How invokeAll() Runs a Batch of Tasks
The invokeAll() method hands every task in the collection to the ExecutorService and blocks the calling thread until each one has either returned a value or thrown an exception. So the call takes about as long as the slowest task, not the sum of all tasks, as long as the pool has a thread for each task.

The method has two forms, and both throw InterruptedException when the calling thread is interrupted while it waits. The timed form is covered in section 3.
| Method | Returns when | Unfinished tasks |
|---|---|---|
| invokeAll(tasks) | Every task is done | None remain |
| invokeAll(tasks, timeout, unit) | Every task is done or the timeout expires | Cancelled |
A dashboard home screen is a good fit. The weather, the latest orders and the news feed come from three different services, and the page renders only when all three are ready. The following example simulates the three calls with different pauses, so the tasks finish in the order orders, news, weather.
static void pause(long ms) {
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(ms));
}
static Callable<String> widget(String name, long ms) {
return () -> {
pause(ms);
return name;
};
}
List<Callable<String>> tasks = List.of(
widget("weather", 300),
widget("orders", 100),
widget("news", 200));
List<Future<String>> results;
try (ExecutorService pool = Executors.newFixedThreadPool(3)) {
results = pool.invokeAll(tasks);
}
List<String> page = results.stream().map(Future::resultNow).toList(); // [weather, orders, news]
We can see that the list follows the order of the tasks, not the order in which they finished. The Future at index 0 always belongs to the first task produced by the collection’s iterator, so we can match each result to its task by position. The resultNow() method (Java 19) returns the value without a checked exception, which fits here because the tasks are already done.
Since Java 19, ExecutorService implements AutoCloseable, so a try-with-resources block shuts the pool down and waits for running tasks. Older code calls shutdown() in a finally block, as described in the ExecutorService shutdown guide.
2. Reading Results and Failures from the Futures
A task that throws an exception does not make invokeAll() throw. The exception is stored in that task’s Future, and the other tasks complete normally. We find out about the failure only when we inspect each Future.
The following example lets the orders service fail. The get() method wraps the original exception in an ExecutionException, whereas state() and exceptionNow() (both Java 19) report the failure without throwing.
static Callable<String> failing(String message) {
return () -> {
throw new IllegalStateException(message);
};
}
List<Callable<String>> mixed = List.of(widget("weather", 50), failing("orders down"), widget("news", 50));
List<Future<String>> outcome;
try (ExecutorService pool = Executors.newFixedThreadPool(3)) {
outcome = pool.invokeAll(mixed);
}
Future.State second = outcome.get(1).state(); // FAILED
String reason = outcome.get(1).exceptionNow().getMessage(); // "orders down"
String value = outcome.get(1).get(); // ExecutionException: IllegalStateException: orders down
List<String> ready = outcome.stream().filter(f -> f.state() == Future.State.SUCCESS).map(Future::resultNow).toList(); // [weather, news]
Filtering by Future.State.SUCCESS is the cleanest way to render the widgets that worked and show a placeholder for the rest. In code that targets Java 8 to 18, we call get() in a loop and catch ExecutionException for each task instead.
3. Using invokeAll() with a Timeout
The timed form returns when all tasks are done or when the timeout expires, whichever comes first. Any task that is still running at that point is cancelled, so every Future in the returned list is still done, but some of them are in the CANCELLED state.
Say a product page must answer within 150 ms. The price and the stock level are fast, but the review service sometimes takes seconds, and the page should render without reviews rather than wait.
List<Callable<String>> slow = List.of(widget("price", 20), widget("stock", 30), widget("reviews", 2000));
List<Future<String>> partial;
try (ExecutorService pool = Executors.newFixedThreadPool(3)) {
partial = pool.invokeAll(slow, 150, TimeUnit.MILLISECONDS);
}
Future.State reviews = partial.get(2).state(); // CANCELLED
boolean cancelled = partial.get(2).isCancelled(); // true
String missing = partial.get(2).get(); // CancellationException
String price = partial.get(0).resultNow(); // "price"
Cancelling a running task interrupts its thread, so the task stops early only if it reacts to interrupts. A task that ignores the interrupt keeps running in the background after invokeAll() returns, and the close() call at the end of the try block waits for it. The InterruptedException guide explains how to write tasks that stop cleanly.
4. Getting the First Result with invokeAny()
The invokeAny() method returns the value of one task that completed successfully, which in practice is the first one to succeed. As soon as it has that value, it cancels the tasks that are still running. Tasks that throw are ignored as long as another task succeeds.
Like invokeAll(), it comes in a plain and a timed form. Because it returns a value instead of futures, it reports failures by throwing.
| Method | Returns | Throws |
|---|---|---|
| invokeAny(tasks) | The first successful result | ExecutionException if no task succeeds |
| invokeAny(tasks, timeout, unit) | The first successful result within the timeout | ExecutionException if all fail, TimeoutException if time runs out |
A download client that knows three mirror servers with the same file is a typical case. It asks all three and uses whichever answers first with a valid response. The following example has a slow mirror, a fast mirror that fails, and a medium one, so the medium mirror wins.
List<Callable<String>> mirrors = List.of(
widget("eu-mirror", 300),
failing("us-mirror refused"),
widget("asia-mirror", 100));
String winner;
try (ExecutorService pool = Executors.newFixedThreadPool(3)) {
winner = pool.invokeAny(mirrors);
}
String used = winner; // "asia-mirror"
The failing mirror answered first, but invokeAny() skipped it because only successful results count. The slow European mirror was still running when the Asian one succeeded, so invokeAny() cancelled it.
The edge cases follow from that rule.
- If every task fails, invokeAny() throws an ExecutionException whose cause is the exception of one of the failed tasks.
- If no task succeeds before the timeout of the timed form, it throws a TimeoutException and cancels the running tasks.
- An empty collection throws an IllegalArgumentException, and a null task throws a NullPointerException.
static String fastest(List<Callable<String>> tasks, long timeoutMs) throws Exception {
try (ExecutorService pool = Executors.newVirtualThreadPerTaskExecutor()) {
return pool.invokeAny(tasks, timeoutMs, TimeUnit.MILLISECONDS);
}
}
String allFail = fastest(List.of(failing("a down"), failing("b down")), 500); // ExecutionException
String tooSlow = fastest(List.of(widget("late", 1000)), 50); // TimeoutException
String noTasks = fastest(List.of(), 50); // IllegalArgumentException
The fastest() helper uses a virtual thread per task, which suits tasks that mostly wait on the network. Notice that the TimeoutException case still returns after about 50 ms, because invokeAny() cancels the late task and the task stops at the interrupt.
5. invokeAll() vs invokeAny() vs submit() vs CompletableFuture
The four options solve related problems, and the choice depends on whether we need every result, any result, or a non-blocking pipeline. The blocking methods are simpler to read, whereas CompletableFuture lets the calling thread continue and combine results later.
| Option | Returns | Blocks the caller | Failed tasks | Best for |
|---|---|---|---|---|
| invokeAll(tasks) | List<Future<T>> in task order | Yes, until all finish | Stored in their Future | A fixed batch where we need every result |
| invokeAny(tasks) | One T | Yes, until one succeeds | Ignored unless all fail | Redundant sources, fastest answer wins |
| submit() in a loop | One Future per call | No | Stored in their Future | Tasks that arrive one by one |
| CompletableFuture.allOf() / anyOf() | CompletableFuture | No | Propagated through the chain | Non-blocking pipelines and callbacks |
Calling submit() for each task and calling get() on each Future gives the same results as invokeAll(), but we write the loop ourselves and must cancel the remaining tasks when we stop early. In Java 25, structured concurrency offers the same all-or-first patterns with automatic cancellation, but it is still a preview feature (JEP 505).
All of these build on the Callable and Future interfaces, and the Java concurrency guide lists the other building blocks.
6. Common Mistakes with invokeAll() and invokeAny()
Most problems with the two methods come from the size of the pool and from tasks that ignore cancellation. Each of the following mistakes compiles and runs, which makes it hard to spot in a code review.
- A pool smaller than the batch runs the tasks in waves. Five tasks of equal length on newFixedThreadPool(2) take about three times as long as one task, so we size the pool for the batch or use virtual threads.
- Calling invokeAll() from inside a task that runs on the same bounded pool can deadlock. The outer task holds a thread while it waits for inner tasks that have no free thread to run on.
- A task that catches InterruptedException and keeps going defeats the timeout and the cancellation in invokeAny(), because it keeps a pool thread busy.
- Changing the task collection while invokeAll() runs gives undefined results, so we pass an immutable list such as List.of(…).
The method also accepts only Callable tasks. To run a Runnable, we wrap it with Executors.callable(), as the FAQ in section 7 shows.
7. invokeAll and invokeAny FAQs
Both methods block the caller, so most questions about them concern the order of the results and what becomes of the tasks that are still running.
7.1. Does invokeAll() return the results in the same order as the tasks?
Yes. The returned list has the same order as the collection’s iterator, no matter which task finishes first. A HashSet of tasks has no defined order, so we pass a List when the order matters.
7.2. Is invokeAll() a blocking call?
Yes. The calling thread waits until every task is done or the timeout expires. If the calling thread is interrupted while it waits, invokeAll() cancels the unfinished tasks and throws InterruptedException. For a non-blocking version, we use CompletableFuture.allOf().
7.3. What happens to the other tasks when invokeAny() returns?
The executor cancels them with cancel(true), which interrupts their threads. Tasks that check the interrupt flag or call blocking methods stop early, while tasks that ignore interrupts run to the end and their results are discarded.
7.4. Can we pass Runnable tasks to invokeAll()?
No, because the parameter type is a collection of Callable. The Executors.callable() method adapts a Runnable and returns null or a fixed value as the result.
Runnable cleanup = () -> {};
List<Callable<Object>> jobs = List.of(Executors.callable(cleanup));
List<Future<Object>> done;
try (ExecutorService pool = Executors.newSingleThreadExecutor()) {
done = pool.invokeAll(jobs);
}
Object result = done.get(0).resultNow(); // null
7.5. Should we use invokeAll() or submit() in a loop?
Use invokeAll() when the whole batch is known up front and we need every result before moving on. Use submit() when tasks arrive over time, or when we want to process each result as soon as it is ready, for example with an ExecutorCompletionService.
8. Conclusion
The invokeAll() method runs a batch of Callable tasks and returns their futures in task order once all of them are done. Failures stay inside each Future, and since Java 19 we can read them with state(), resultNow() and exceptionNow() instead of catching ExecutionException.
The timed form cancels whatever is still running when the time is up. The invokeAny() method returns the first successful result, cancels the rest, and throws only when every task fails or the timeout expires. Both methods work best with a try-with-resources executor and with tasks that respond to interrupts.
9. References
- ExecutorService Javadoc (Java 25)
- Future Javadoc (Java 25)
- Executors Javadoc (Java 25)
- JEP 505, Structured Concurrency (Fifth Preview)
Happy Learning !!
Hi,
Is invokeAll() method is blocking. I mean does it block and return list of future only when all operations all completed.
NOTE:- I AM NOT TAKING ABOUT Future.get() METHOD. I KNOW IT IS BLOCKING.
Yes, invokeAll() blocks the calling thread until all the tasks have finished executing or until an exception is thrown.
Hi Author, Very good post. I want to ask one question that is. Suppose I have submitted list of tasks to invokeAll() method. The list contains 5 elements. Suppose task 2 and 3 is completed and 1,4 and 5 task is still in progress and I call list.get(2).get() then call to get() method of future element which is 2nd element will still stuck just because 1,4,5 task are not completed yet. In other words even if you call get() method on future element whose task is finished, will end up get() method call stuck just because other tasks are not completed?
No, calling the get() method on a Future object whose corresponding task has already completed will not block if the other tasks are still in progress. The get() method will return the result immediately if the task has finished executing.