Thread Pool Overview
A thread pool manages a collection of worker threads that are reused to execute multiple tasks. Instead of creating new threads for each task, the thread pool reuses existing threads, reducing the overhead of thread creation and destruction.
Core Interfaces and Implementations
ExecutorService Interface
The ExecutorService interface extends Executor and provides methods for managing thread pool lifecycle and task execution. It allows clients to submit tasks for execution and provides methods to determine whether the pool has finished processing.
ThreadPoolExecutor Class
This is the core implementation class that extends AbstractThreadPoolExecutor. It provides the actual logic for creating, managing, and recycling worker threads.
Executors Utility Class
The Executors class provides factory methods for creating various types of thread pools with predefined configurations:
- Cached Thread Pool - Creates threads dynamically as needed, with a 60-second timeout: ```
Executors.newCachedThreadPool();
- Fixed Thread Pool - Maintains a fixed number of persistent threads: ```
Executors.newFixedThreadPool(20);
- Single Thread Executor - Uses exactly one worker thread: ```
Executors.newSingleThreadExecutor();
- Scheduled Thread Pool - Supports delayed and periodic task execution: ```
Executors.newScheduledThreadPool(20);
ScheduledThreadPoolExecutor Methods
This specialized executor supports time-based task execution:
schedule()- Executes a task once after a specified delayscheduleAtFixedRate()- Executes tasks at fixed time intervalsscheduleWithFixedDelay()- Executes tasks with a fixed delay between completion and next execution
Thread Pool Configuration Parameters
The ThreadPoolExecutor constructor accepts the following parameters:
- corePoolSize: The minimum number of threads kept alive in the pool. These core threads are never reclaimed.
- maximumPoolSize: The maximum number of threads the pool can contain, including core threads.
- keepAliveTime: The duration idle non-core threads remain in the pool before being terminated.
- timeUnit: The time unit for the
keepAliveTimeparameter. - workQueue: The blocking queue holding tasks waiting to be executed when no threads are available.
- threadFactory: Creates new threads. Custom implementations can set thread names and daemon status.
Example of a custom thread factory:
class CustomThreadFactory implements ThreadFactory {
private final AtomicInteger threadCounter = new AtomicInteger(1);
private final String threadNamePrefix;
public CustomThreadFactory(String prefix) {
this.threadNamePrefix = prefix;
}
@Override
public Thread newThread(Runnable task) {
Thread thread = new Thread(task, threadNamePrefix + threadCounter.getAndIncrement());
if (thread.isDaemon()) {
thread.setDaemon(false);
}
if (thread.getPriority() != Thread.NORM_PRIORITY) {
thread.setPriority(Thread.NORM_PRIORITY);
}
return thread;
}
}
- rejectedExecutionHandler: The policy applied when the pool cannot accept a new task due to saturation.
Rejection Policies
When the thread pool is saturated and the work queue is full, the following policies determine how new tasks are handled:
- AbortPolicy: Throws a
RejectedExecutionException - DiscardPolicy: Silently drops the task without notification
- DiscardOldestPolicy: Removes and discards the oldest unprocessed task
- CallerRunsPolicy: Executes the task in the caller's thread
Custom rejection handlers can be created by implementing the RejectedExecutionHandler interface.
Execution Flow
When a task is submitted via execute() or submit(), the thread pool follows this decision process:
- Check for idle threads: If an idle thread is available, the task is assigned immediately for execution.
- Compare against core pool size: If the current thread count is less than
corePoolSize, a new core thread is created to handle the task usingaddWorker(). - Queue the task: If core thread are fully utilized, the task is added to the blocking work queue for waiting.
- Expand to maximum pool size: If the queue is full and the current thread count is less than
maximumPoolSize, a new non-core thread is created to process queued tasks. - Apply rejection policy: If both the pool and queue are saturated, the rejection policy is invoked.
This priority sequence ensures efficient resource utilization: core threads first, then queuing, and finally non-core thread creation.
Thread Pool States
The ThreadPoolExecutor maintains state information using an atomic control variable:
- RUNNING: Accepts new tasks and processes queued tasks. Transitions to SHUTDOWN via
shutdown()or to STOP viashutdownNow(). - SHUTDOWN: No longer accepts new tasks but continues processing the work queue.
- STOP: Does not accept new tasks and does not process the queue. Interrupts running tasks.
- TIDYING: All tasks have terminated and the queue is empty. Implements
terminated()hook. - TERMINATED: The pool has completely shut down.
Internal State Representation
The thread pool uses an AtomicInteger ctl field to store both state and worker count. The high-order 3 bits encode the thread state, while the remaining 29 bits store the active worker count.