Medium🚀 Mastering Java ExecutorService: Fixed Thread Pool, Cached Thread Pool, Single Thread Executor & Work Stealing Pool | by curiouswomen | Medium
Sitemap
curiouswomen

curiouswomen

I am here to share my experience being it a technical, non-technical or daily life experience that I am curious and thrive a positive change.

🚀 Mastering Java ExecutorService: Fixed Thread Pool, Cached Thread Pool, Single Thread Executor & Work Stealing Pool

4 min readJun 15, 2026

--

Introduction

One of the biggest mistakes beginners make in Java multithreading is creating threads directly:

new Thread(task).start();

This works for a few tasks.

But what happens when:

  • 1,000 users hit your API?
  • 10,000 background jobs need processing?
  • Hundreds of database operations execute simultaneously?

Creating a new thread for every task quickly becomes expensive.

This is exactly why Java introduced the Executor Framework in Java 5 (2004) as part of the java.util.concurrent package.

The goal was simple:

Reuse threads instead of creating new ones repeatedly.

Why ExecutorService Was Introduced

Imagine a company.

Without ExecutorService:

New Task
↓
Hire New Employee
↓
Employee Does Work
↓
Employee Fired

This is inefficient.

Instead:

Company Maintains Workers
↓
Tasks Assigned
↓
Workers Reused

This is exactly how thread pools work.

What Is ExecutorService?

ExecutorService is an interface responsible for:

  • Managing thread pools
  • Scheduling task execution
  • Reusing worker threads
  • Returning task results through Future

Example:

ExecutorService executor =
Executors.newFixedThreadPool(3);

1. Fixed Thread Pool

Creation

ExecutorService executor =
Executors.newFixedThreadPool(3);

How It Works

Pool size remains fixed.

Suppose:

Pool Size = 3

Tasks:

Task1
Task2
Task3
Task4
Task5

Execution:

Thread1 → Task1
Thread2 → Task2
Thread3 → Task3
Task4 → Waiting
Task5 → Waiting

Real-Life Analogy

Restaurant with 3 chefs.

3 Orders → Cooking
10 Orders →
3 Cooking
7 Waiting

Advantages

  • Predictable resource usage
  • Stable memory consumption
  • Prevents thread explosion

Production Use Cases

  • REST API processing
  • Database operations
  • Backend microservices

2. Single Thread Executor

Creation

ExecutorService executor =
Executors.newSingleThreadExecutor();

How It Works

Only one worker thread exists.

Tasks execute:

Task1
↓
Task2
↓
Task3
↓
Task4

Strictly sequential.

Why Not Use Normal Code?

Because tasks may be submitted from multiple threads, but execution order remains guaranteed.

Real-Life Analogy

One cashier counter.

Customer1
Customer2
Customer3

Nobody skips the queue.

Production Use Cases

  • Audit logging
  • File writing
  • Bank transaction logs
  • Ordered event processing

3. Cached Thread Pool

Creation

ExecutorService executor =
Executors.newCachedThreadPool();

How It Works

Unlike Fixed Thread Pool:

No Fixed Size

If new tasks arrive:

Create Thread

If existing idle thread exists:

Reuse Thread

Idle threads are automatically removed after a period of inactivity.

Real-Life Analogy

Uber Drivers.

More Customers
↓
More Drivers
Less Customers
↓
Drivers Leave

Advantages

  • Good for short-lived tasks
  • Dynamic scaling

Danger

Suppose:

100000 Tasks

arrive suddenly.

Get curiouswomen’s stories in your inbox

Join Medium for free to get updates from this writer.

Cached Thread Pool may create:

100000 Threads

leading to memory issues.

Production Use Cases

  • Lightweight background jobs
  • Burst traffic workloads
  • Temporary asynchronous tasks

4. Work Stealing Pool

Creation

ExecutorService executor =
Executors.newWorkStealingPool();

Internally:

ForkJoinPool

is used.

Why Was It Created?

Consider:

Thread1 → 100 Tasks
Thread2 → 5 Tasks
Thread3 → 0 Tasks

Traditional pools:

Thread2 Idle
Thread3 Idle
Thread1 Overloaded

Poor CPU utilization.

Work Stealing Solution

Idle threads steal work from busy threads.

Before:

T1: ████████████
T2: ██
T3:

After Stealing:

T1: ██████
T2: █████
T3: █████

Load becomes balanced.

Flatmates Analogy 😄

Imagine:

Rahul → 12 utensils
Aman → 1 utensil
Vikas → 0 utensils

After Aman finishes:

Aman steals utensils
from Rahul.

Now everyone contributes.

This is exactly how Work Stealing works.

Production Use Cases

ForkJoinPool is ideal for:

  • Parallel sorting
  • Large data processing
  • Recursive algorithms
  • Parallel streams

Example:

list.parallelStream()

Internally uses:

ForkJoinPool

ExecutorService Lifecycle

Every thread pool should be properly shut down.

shutdown()

executor.shutdown();

Meaning:

No New Tasks
Finish Existing Tasks

awaitTermination()

executor.awaitTermination(
10,
TimeUnit.SECONDS
);

Meaning:

Wait For Existing Tasks
Maximum Wait = 10 Seconds

shutdownNow()

executor.shutdownNow();

Meaning:

Stop Everything Immediately
Interrupt Running Threads

Comparison Table

Executor TypeThreadsQueueingBest Use CaseFixed Thread PoolFixedYesBackend APIsSingle Thread Executor1YesOrdered ProcessingCached Thread PoolDynamicNo Fixed LimitShort-lived TasksWork Stealing PoolDynamicWork StealingCPU Intensive Computations

Fixed vs Cached Thread Pool

Fixed

Controlled
Predictable
Memory Safe

Cached

Flexible
Scalable
Can Create Too Many Threads

Fixed vs Work Stealing Pool

Fixed Thread Pool

Shared Queue

Work Stealing Pool

Per Thread Queue
Idle Threads Steal Work

Result:

Better CPU Utilization

Modern Java (Java 21+)

Java 21 introduced Virtual Threads.

Instead of:

Executors.newFixedThreadPool()

many modern applications can use:

Executors.newVirtualThreadPerTaskExecutor();

This allows:

Millions of Lightweight Threads

without creating millions of operating system threads.

Interview Questions

Why use ExecutorService instead of creating threads directly?

Creating threads is expensive. ExecutorService reuses threads and manages lifecycle efficiently.

Difference between Fixed and Cached Thread Pool?

Fixed limits thread count, while Cached creates threads dynamically as needed.

When should Work Stealing Pool be used?

For divide-and-conquer and CPU-intensive workloads where tasks can be processed in parallel.

Which pool is most common in backend applications?

Fixed Thread Pool is commonly used for APIs and service requests.

Key Takeaway

ExecutorService was introduced to solve one major problem:

Thread creation is expensive.

Different thread pools solve different problems:

Fixed Thread Pool
→ Stable Backend Processing
Single Thread Executor
→ Ordered Execution
Cached Thread Pool
→ Dynamic Workloads
Work Stealing Pool
→ Maximum CPU Utilization

Understanding when to use each pool is a key skill for Java backend developers and is frequently tested in SDE interviews.

--

--

curiouswomen

Written by curiouswomen

I am here to share my experience being it a technical, non-technical or daily life experience that I am curious and thrive a positive change.