How to run tasks in parallel
Short answer
An ExecutorService is a thread pool you hand tasks to. submit() returns a Future — a "promise" of a result — and get() waits for it:
int slowSquare(int n) throws InterruptedException {
Thread.sleep(200);
return n * n;
}
void main() throws Exception {
try (ExecutorService pool = Executors.newFixedThreadPool(3)) {
Future<Integer> a = pool.submit(() -> slowSquare(2));
Future<Integer> b = pool.submit(() -> slowSquare(3));
Future<Integer> c = pool.submit(() -> slowSquare(4));
IO.println(a.get() + b.get() + c.get());
}
}Output:
29The three 200 ms tasks run at the same time, so the program waits about 200 ms, not 600. Since Java 19 an ExecutorService can be closed with try-with-resources: it waits for the tasks and stops the threads.
A list of tasks
invokeAll() starts all tasks and returns a list of Futures in the same order:
void main() throws Exception {
List<Callable<String>> tasks = List.of(
() -> "report A",
() -> "report B",
() -> "report C");
try (var pool = Executors.newFixedThreadPool(2)) {
for (Future<String> future : pool.invokeAll(tasks)) {
IO.println(future.get());
}
}
}Output:
report A
report B
report CAn error in a task
An exception from a task is not lost: get() throws an ExecutionException, and the original exception is in getCause():
void main() throws InterruptedException {
try (var pool = Executors.newSingleThreadExecutor()) {
Future<Integer> future = pool.submit(() -> Integer.parseInt("oops"));
try {
future.get();
} catch (ExecutionException e) {
IO.println("Task failed: " + e.getCause().getMessage());
}
}
}Output:
Task failed: For input string: "oops"Common mistake
Calling get() right after each submit(). The program waits for the first task before sending the second, and nothing runs in parallel. Submit all the tasks first, then collect the results.
Functions used in this recipe
- Executors.newFixedThreadPool() — Creates a pool with a given number of threads.
- ExecutorService.submit() — Sends a task to the pool and immediately returns a `Future` for its result.
- Future.get() — Waits for a task to finish and returns its result.
- ExecutorService — A service that runs tasks on a pool of threads.