Skip to content
Dev Dump

Java Executor Framework

The ExecutorService is an interface that represents an asynchronous execution mechanism. It provides methods for:

  • Submitting tasks

  • Controlling thread lifecycle

  • Gracefully shutting down threads

It decouples task submission from the details of how each task will be executed.

  • Simplifies thread management

  • Allows task scheduling and control

  • Better performance via thread pooling

  • Reduces boilerplate code

MethodDescription
submit()Submits a task for execution and returns a Future
execute()Submits a Runnable task for execution (no result)
shutdown()Initiates an orderly shutdown
shutdownNow()Attempts to stop all actively executing tasks
awaitTermination()Waits for the executor to terminate

Use Executors.newFixedThreadPool(n) where n = # of CPU cores.

Why?

  • Prevents excessive context switching.
  • Keeps CPU fully utilized.
InterfaceDescription
ExecutorBase interface with execute(Runnable)
ExecutorServiceAdds lifecycle & submit() (returns Future) support
ScheduledExecutorServiceAdds task scheduling with delay/periodic support

47ca9eff1c1ae33fa1853d2c7d343f3a_MD5

Thread pool = group of pre-initialized threads that execute tasks.

Benefits:

  • Reduces thread creation cost
  • Controls concurrency
  • Efficient CPU and memory usage

a7c018fdab91b4e5b4d42fbaae625cfb_MD5

2758cd23f783dc9a26e68435caecd407_MD5

faecfc4f6ce4328d13984828a4759f9e_MD5

  1. Core Pool Size - Minimum threads always kept alive.
  2. Max Pool Size - Max threads if needed.
  3. Keep-Alive Time - Timeout for idle threads beyond core.
  4. Work Queue - Tasks stored when all threads are busy.
  5. Rejection Policies - Decide what happens when queue is full.

ExecutorService pool = new ThreadPoolExecutor(
3, 5, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10)
);
for (int i = 0; i < 10; i++) {
final int id = i;
pool.submit(() -> {
System.out.println("Task " + id + " by " + Thread.currentThread().getName());
});
}
pool.shutdown();

PolicyBehavior
AbortPolicy (default)Throws RejectedExecutionException
CallerRunsPolicyExecutes task in calling thread
DiscardPolicySilently drops task
DiscardOldestPolicyDrops oldest unhandled task

  • Splits tasks into subtasks recursively
  • Uses work-stealing to load-balance
class SumTask extends RecursiveTask<Long> {
int[] arr; int start, end;
protected Long compute() {
if (end - start <= 10) {
return Arrays.stream(arr, start, end).sum();
}
int mid = (start + end)/2;
SumTask left = new SumTask(arr, start, mid);
SumTask right = new SumTask(arr, mid, end);
left.fork(); right.fork();
return left.join() + right.join();
}
}
  • Recursive tasks like merge sort, matrix ops, recursive searches.

Used for:

  • Delayed tasks
  • Periodic tasks
scheduler.schedule(() -> {
System.out.println("Runs once after 3s");
}, 3, TimeUnit.SECONDS);
scheduler.scheduleAtFixedRate(() -> {
System.out.println("Runs every 2s");
}, 1, 2, TimeUnit.SECONDS);
scheduler.scheduleWithFixedDelay(() -> {
System.out.println("Delay between runs");
}, 1, 3, TimeUnit.SECONDS);

TypeBehavior
FixedRateStarts every N seconds regardless of task time
FixedDelayWaits N seconds AFTER previous completes

Analogy:

  • FixedRate = Set a timer to toast every 5 minutes ⏲️
  • FixedDelay = Wait 5 mins after toast finishes 🧈

  1. Use fixed pools for CPU tasks
  2. Use cached pools for short-lived I/O tasks
  3. Always shutdown() executor
  4. Handle RejectedExecutionException
  5. Monitor using ThreadPoolExecutor.getQueue() etc.

Executor TypeBest ForSchedulingAuto-ScalingNotes
FixedThreadPoolCPU tasksFixed threads
CachedThreadPoolShort-lived async tasksUnbounded pool
SingleThreadExecutorSequential task processingOne thread only
ScheduledThreadPoolExecutorScheduled/periodic tasksSupports delay/fixed rate
ForkJoinPoolParallel recursive tasks✅ (adaptive)Uses work stealing
  • newFixedThreadPool(int) – Reuses fixed # of threads
  • newCachedThreadPool() – Creates threads as needed
  • newSingleThreadExecutor() – One thread
  • newScheduledThreadPool(int) – Schedule with delay