How do I use ExecutorService.invokeAll?

Hello! ExecutorService.invokeAll is a powerful method when you have a collection of tasks and need to wait until every single one of them finishes before moving forward.

Here’s a breakdown of how it works and how to use it effectively.

What does invokeAll do?

  1. Executes a collection of tasks: It takes a Collection of Callable<T> objects.
  2. Blocks until completion: Unlike submit(), which returns immediately, invokeAll is blocking. It will not return until all tasks in the collection have completed (either normally or by throwing an exception).
  3. Returns a list of Futures: It returns a List<Future<T>> that holds the results (or exceptions) of the tasks, in the same order they were provided in the input collection.

Basic Usage Pattern

Here is a clean example of how to implement it:

package org.kodejava.util.concurrent;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;

public class InvokeAllDemo {
    public static void main(String[] args) {
        // 1. Create your ExecutorService
        try (ExecutorService executor = Executors.newFixedThreadPool(3)) {

            // 2. Define your tasks (Callable returns a value)
            List<Callable<String>> tasks = Arrays.asList(
                    () -> { Thread.sleep(500); return "Result A"; },
                    () -> { Thread.sleep(1000); return "Result B"; },
                    () -> { Thread.sleep(200); return "Result C"; }
            );

            try {
                // 3. Invoke all tasks. Execution stops here until all are done.
                System.out.println("Executing tasks...");
                List<Future<String>> futures = executor.invokeAll(tasks);

                // 4. Process the results
                for (Future<String> future : futures) {
                    // Future.get() will not block here because invokeAll already waited
                    System.out.println("Task output: " + future.get());
                }
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        } // Executor closes automatically with try-with-resources (Java 19+)
    }
}

Important Considerations

  • Order Preservation: The returned List<Future> maintains the same order as the input task list. futures.get(0) will always correspond to tasks.get(0).
  • Timeouts: There is an overloaded version: invokeAll(tasks, timeout, unit). If the timeout expires, tasks that haven’t finished are canceled, and the method returns the list of futures (some will be marked as canceled).
  • Exceptions: If a task throws an exception, invokeAll doesn’t fail. Instead, that specific Future.get() will throw an ExecutionException.
  • Blocking Behavior: Since invokeAll blocks the calling thread, avoid calling it on a thread that needs to stay responsive (like a UI thread or a primary event loop) without careful planning.

When to use it vs invokeAny?

  • Use invokeAll when you need the results of everything you started.
  • Use invokeAny when you have multiple ways to get a result, and you only care about the first one that finishes successfully (it cancels the rest).

Happy coding! If you’re working within a Spring environment, you might also want to look into @Async for higher-level abstraction, but for raw concurrency control, invokeAll is a classic choice.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.