← Back to Advanced Java Course | Chapter 2: Multithreading | Lesson 6 of 8

Java Executor Framework

What is the Executor Framework?

The Executor framework is a built-in concurrency library introduced in Java 5 that manages thread pools, task queues, and scheduling automatically. It removes the need to manually create, start, and track Thread objects yourself, which becomes error-prone once an application needs more than a handful of concurrent tasks.

Example: What is the Executor Framework?

java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
	public static void main(String[] args) {
		ExecutorService executor = Executors.newFixedThreadPool(2);
		executor.submit(() -> System.out.println("Task managed by the pool"));
		executor.shutdown();
	}
}

Single Thread Executor

A Single Thread Executor manages exactly one background worker thread and executes every submitted task sequentially, strictly in the order those tasks were submitted. This gives you the safety of single-threaded execution — no race conditions between tasks — while still keeping that work off the calling thread.

Example: Single Thread Executor

java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
	public static void main(String[] args) {
		ExecutorService executor = Executors.newSingleThreadExecutor();
		executor.submit(() -> System.out.println("Task 1"));
		executor.submit(() -> System.out.println("Task 2")); // runs after Task 1, same worker
		executor.shutdown();
	}
}

Fixed Thread Pool

A Fixed Thread Pool manages a set number of worker threads and hands submitted tasks to whichever thread is free, reusing each thread across many tasks instead of spawning a new one every time. This bounds resource usage predictably, which matters a lot under heavy or bursty load.

Example: Fixed Thread Pool

java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
	public static void main(String[] args) {
		ExecutorService executor = Executors.newFixedThreadPool(3);
		for (int i = 1; i <= 5; i++) {
			int taskId = i;
			executor.submit(() -> System.out.println("Task " + taskId));
		}
		executor.shutdown();
	}
}

Submitting Callable Tasks

Unlike the Runnable interface, the Callable interface represents tasks that can return a computed result and can throw checked exceptions during execution. Submitting a Callable to an executor gets you back a Future you can use to retrieve that result once the task finishes.

Example: Submitting Callable Tasks

java
import java.util.concurrent.*;
public class Main {
	public static void main(String[] args) throws Exception {
		ExecutorService executor = Executors.newSingleThreadExecutor();
		Callable<Integer> task = () -> 10 + 20;
		Future<Integer> future = executor.submit(task);
		System.out.println(future.get());
		executor.shutdown();
	}
}

Shutting Down Executors

Always explicitly shut down executors you create, since their worker threads would otherwise keep the JVM alive indefinitely. Call shutdown() to stop accepting new tasks while letting already-submitted ones finish normally, or shutdownNow() when you need everything to stop immediately.

Example: Shutting Down Executors

java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
	public static void main(String[] args) {
		ExecutorService executor = Executors.newFixedThreadPool(2);
		executor.submit(() -> System.out.println("Task running"));
		executor.shutdown(); // stop accepting new tasks, finish existing ones
	}
}

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.