ExecutorService
Manage thread pools and execute tasks asynchronously with ExecutorService and Future.
Overview
Key Points
- •Executors.newFixedThreadPool(n): pool of exactly n threads
- •Executors.newSingleThreadExecutor(): one thread, tasks run sequentially
- •Executors.newCachedThreadPool(): creates threads as needed, reuses idle ones
- •executor.submit(runnable) or submit(callable) returns a Future
- •Future.get() blocks until the result is ready
- •Future.isDone() checks without blocking
- •executor.shutdown() stops accepting new tasks; awaits completion
- •executor.shutdownNow() attempts to cancel running tasks immediately
- •awaitTermination(timeout, unit) waits for the pool to stop
Examples
Fixed thread pool with Runnable
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 1; i <= 6; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.printf("Task %d started on %s%n", taskId, Thread.currentThread().getName());
try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
System.out.printf("Task %d done%n", taskId);
});
}
executor.shutdown(); // no more tasks accepted
executor.awaitTermination(5, TimeUnit.SECONDS); // wait for all tasks
System.out.println("All tasks complete");
}
}With 3 threads and 6 tasks, the pool runs 3 tasks concurrently. When a thread finishes a task, it picks the next one from the queue. This is much more efficient than creating 6 threads.
Callable and Future: getting results
import java.util.concurrent.*;
import java.util.*;
public class Main {
static long fibonacci(int n) {
if (n <= 1) return n;
long a = 0, b = 1;
for (int i = 2; i <= n; i++) { long c = a + b; a = b; b = c; }
return b;
}
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(4);
int[] inputs = {40, 42, 44, 46};
List<Future<Long>> futures = new ArrayList<>();
for (int n : inputs) {
futures.add(executor.submit(() -> fibonacci(n)));
}
// Collect results (get() blocks until each future is done)
for (int i = 0; i < inputs.length; i++) {
System.out.printf("fib(%d) = %d%n", inputs[i], futures.get(i).get());
}
executor.shutdown();
}
}submit(Callable) returns Future<Long>. Calling get() waits for that specific task to finish. The tasks may complete in any order, but we collect results in submission order.
invokeAll: wait for a batch of tasks
import java.util.concurrent.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(3);
List<Callable<String>> tasks = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
final int id = i;
tasks.add(() -> {
Thread.sleep(id * 50L);
return "Result from task " + id;
});
}
// invokeAll blocks until all tasks are done, then returns their Futures
List<Future<String>> results = executor.invokeAll(tasks, 10, TimeUnit.SECONDS);
for (Future<String> f : results) {
System.out.println(f.get()); // each future is already done
}
executor.shutdown();
}
}invokeAll() submits all tasks and blocks until all complete (or the timeout expires). Unlike calling get() on each Future individually, invokeAll guarantees all are done before you iterate.
Common Mistakes
- Not calling executor.shutdown(). The JVM will not exit because the thread pool threads keep the JVM alive even after main() returns
- Calling future.get() immediately in a loop before all tasks are submitted. This defeats the purpose: you wait for each task to finish before submitting the next, achieving no concurrency
- Ignoring ExecutionException from future.get(). When a Callable throws, the exception is wrapped in ExecutionException. Always catch it and call getCause() to see the original exception
Note
For CPU-bound tasks, set the thread pool size to the number of available processors: Runtime.getRuntime().availableProcessors(). For I/O-bound tasks, use a larger pool because threads spend most time waiting.
Exercises
Write Java code and click Run to execute it instantly. When your output looks correct, mark as done.
Completed: 0 / 4 exercises
Knowledge Check
Question 1 of 4