The Executor framework abstracts that by separating task submission from task execution.
A simple interface representing something that executes tasks.
public interface Executor {
void execute(Runnable command);
}
Defines the basic interface for task submission — a Runnable task.
Represents the idea of decoupling task submission from task execution.
Does not manage threads, lifecycle, or results — just executes a given task.
Executor executor = command -> new Thread(command).start();
executor.execute(() -> System.out.println("Task executed"));
Extends Executor with advanced thread management and task control features.
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit);
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks);
<T> T invokeAny(Collection<? extends Callable<T>> tasks);
}
Controls startup and shutdown.
Provides graceful termination via:
shutdown()
shutdownNow()
awaitTermination()
Manages a pool of reusable worker threads.
Handles scheduling and reuse of threads for efficiency.
Queues submitted tasks and executes them asynchronously.
Decides task ordering and concurrency based on configuration.
Accepts both Runnable and Callable tasks.
Returns Future objects to track completion and retrieve results.
invokeAll() – waits for all tasks to complete.
invokeAny() – returns the result of the first successful task.
ExecutorService pool = Executors.newFixedThreadPool(4);
Future<Integer> result = pool.submit(() -> {
// Some computation
return 42;
});
System.out.println(result.get()); // waits and prints 42
pool.shutdown();
+--------------------+
| Executor |
|--------------------|
| void execute(R) |
+---------+----------+
|
v
+--------------------+
| ExecutorService |
|--------------------|
| + submit(...) |
| + shutdown() |
| + invokeAll(...) |
+---------+----------+
|
v
+--------------------+
| ThreadPoolExecutor |
| ScheduledThreadPool|
| ForkJoinPool |
+--------------------+
public interface Callable<V> {
V call() throws Exception;
}
Similar to Runnable, but with additional features:
Can return a value (V).
Can throw a checked exception.
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Think of Future as:
A promise that a result will be available later.
A way to block, check, or cancel the computation.
ExecutorService pool = Executors.newFixedThreadPool(2);
Callable<Integer> task = () -> {
Thread.sleep(1000);
return 42;
};
Future<Integer> future = pool.submit(task);
System.out.println("Task submitted...");
// Wait for result
Integer result = future.get();
System.out.println("Result: " + result);
pool.shutdown();
Output:
Task submitted...
Result: 42
Future is blocking — you call get() and wait.
It doesn’t support chaining or callbacks.
Newer APIs:
CompletableFuture: supports chaining, composition, async callbacks.
FutureTask: a concrete implementation of both Runnable and Future.