A Thread Pool is a collection of pre-created, reusable threads that are kept ready to perform tasks. Instead of creating a new thread every time you need to run something (which is costly in terms of memory and CPU), a thread pool maintains a fixed number of threads. When a task is submitted:
- If a thread is free, it immediately picks up the task and runs it.
- If all threads are busy, the task waits in a queue until a thread becomes available.
- After finishing a task, the thread does not die. It goes back to the pool and waits for the next task.
Real World Analogy
Think of a call center:
- A company has 10 operators (thread pool size = 10).
- When customers call, available operators handle them.
- If all operators are busy, new customers wait in a queue until someone is free.
- Operators don’t leave after one call, they stay and handle more calls.
Benefits of Thread Pool
- Better Performance: Threads are reused instead of being created and destroyed repeatedly.
- Faster Response Time: Tasks don’t need to wait for a new thread to be created.
- Reusability: Threads remain alive after finishing tasks and are reused for future tasks.
- Resource Management: Limits the number of concurrent threads, preventing OutOfMemoryError or CPU overload.
Thread Pool Initialization
When we initialize a thread pool:
- A fixed number of worker threads are created (e.g., 3).
- These threads are kept idle, waiting for tasks.
- A task queue is set up to hold submitted tasks until a worker is free.
Thread Pool Working
Here is the step by step working of thread pool.
Step 1: Idle State
- Tasks are submitted and placed in the Task Queue.
- Worker threads exist but are idle until work arrives.

Step 2: Task Assignment
- Each idle thread picks a task from the queue.
- Example: Thread 1 -> Task 1, Thread 2 -> Task 2, Thread 3 -> Task 3
- Remaining tasks (Task 4, Task 5) wait in the queue.

Step 3: Thread Reuse
- Once a thread completes its current task, it becomes idle again.
- It immediately takes the next waiting task from the queue.
- Example: Thread 1 -> Task 4, Thread 2 -> Task 5, Thread 3 -> Idle (no tasks left)

Code Example: Simple Thread Pool
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
// Worker thread that executes tasks
class Worker extends Thread {
private final BlockingQueue<Runnable> taskQueue;
private static final Runnable POISON_PILL = () -> {};
public Worker(BlockingQueue<Runnable> queue, String name) {
super(name);
this.taskQueue = queue;
}
public void run() {
try {
while (true) {
Runnable task = taskQueue.take();
if (task == POISON_PILL) {
break; // stop worker
}
try {
task.run();
} catch (Exception e) {
System.out.println("Task error: " + e.getMessage());
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
// Thread Pool Implementation
class SimpleThreadPool {
private final BlockingQueue<Runnable> taskQueue;
private final Worker[] workers;
private volatile boolean isShutdown = false;
private static final Runnable POISON_PILL = () -> {};
public SimpleThreadPool(int poolSize) {
taskQueue = new LinkedBlockingQueue<>();
workers = new Worker[poolSize];
for (int i = 0; i < poolSize; i++) {
workers[i] = new Worker(taskQueue, "Worker-" + (i + 1));
workers[i].start();
}
}
// Submit task
public void submit(Runnable task) throws InterruptedException {
if (!isShutdown) {
taskQueue.put(task); // safer than offer()
} else {
throw new IllegalStateException("ThreadPool is shutdown");
}
}
// Shutdown pool gracefully
public void shutdown() {
isShutdown = true;
// Send stop signal to all workers
for (int i = 0; i < workers.length; i++) {
try {
taskQueue.put(POISON_PILL);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// Wait for workers to finish
for (Worker worker : workers) {
try {
worker.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
// Test Program
public class ThreadPoolDemo {
public static void main(String[] args) {
SimpleThreadPool pool = new SimpleThreadPool(3);
try {
for (int i = 1; i <= 5; i++) {
int taskId = i;
pool.submit(() -> {
System.out.println("Executing Task " + taskId + " by " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
pool.shutdown();
System.out.println("All tasks completed.");
}
}
Output:

Thread Pool methods
Here is the table of thread pool methods.
| Method | Purpose |
|---|---|
| submit(Runnable task) | Adds a task into the queue for execution by worker threads. |
| shutdown() | Gracefully stops the thread pool → no new tasks accepted, workers stop after finishing current tasks. |
| shutdownNow() (optional) | Immediately interrupts workers and clears pending tasks. |
| getQueueSize() (optional) | Returns how many tasks are waiting in the queue. |
| getActiveCount() (optional) | Returns number of threads currently executing tasks. |