Codectionary / Developer documentation / Java

ExecutorService & Thread Pools

ExecutorService, from java.util.concurrent, manages a pool of reusable threads, letting you submit tasks without manually creating and managing individual Thread objects. This avoids the overhead of constantly creating new threads and gives you control over how many run concurrently. Executors provides convenient factory methods for common pool configurations, like a fixed-size pool or a single-threaded executor.

Syntax

ExecutorService executor = Executors.newFixedThreadPool(n);

Examples

Basic ExecutorService Usage

Submitting tasks to a thread pool instead of manually managing Thread objects.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

ExecutorService executor = Executors.newFixedThreadPool(3);

for (int i = 1; i <= 5; i++) {
    int taskId = i;
    executor.submit(() -> {
        System.out.println("Running task " + taskId + " on " + Thread.currentThread().getName());
    });
}

executor.shutdown();  // stop accepting new tasks, let submitted ones finish

Getting Results with Future

submit() returns a Future, letting you retrieve a task's result once it completes.

import java.util.concurrent.*;

ExecutorService executor = Executors.newFixedThreadPool(2);

Future<Integer> future = executor.submit(() -> {
    Thread.sleep(100);
    return 42;
});

try {
    Integer result = future.get();  // blocks until the task finishes
    System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {
    System.out.println("Task failed: " + e.getMessage());
}

executor.shutdown();

Different Executor Types

Executors provides several pool configurations for different needs.

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;

ExecutorService fixedPool = Executors.newFixedThreadPool(4);   // exactly 4 threads
ExecutorService cachedPool = Executors.newCachedThreadPool();   // grows/shrinks as needed
ExecutorService singleThread = Executors.newSingleThreadExecutor(); // one thread, tasks run in order

singleThread.submit(() -> System.out.println("Task A"));
singleThread.submit(() -> System.out.println("Task B"));  // guaranteed to run after A

fixedPool.shutdown();
cachedPool.shutdown();
singleThread.shutdown();

Best practices

  • Always call shutdown() (or shutdownNow() for immediate termination) when done with an ExecutorService - forgetting to do so leaves threads running indefinitely
  • Use newFixedThreadPool() for a predictable, bounded number of concurrent tasks, protecting your system from unbounded thread creation
  • Handle both InterruptedException and ExecutionException when calling future.get(), since either can occur if the task fails or is interrupted
  • Prefer ExecutorService over manually creating raw Thread objects for any real application - it is more efficient and easier to manage correctly

Runtime & versions

Java Virtual Machine. Feature availability depends on the language and runtime version used by your project; consult the official reference below.

Java Language Specification

Specifications & further reading