Understanding Java ThreadPoolExecutor Rejection Policies
This article explains the four rejection policies of Java's ThreadPoolExecutor, shows the default AbortPolicy behavior, provides code examples for each policy, and demonstrates how to configure custom policies using both raw ThreadPoolExecutor and Spring's ThreadPoolTaskExecutor.
Thread pools are commonly used in Java applications, but many developers are unfamiliar with the rejection policies that come into play when the task queue is full and the pool has reached its maximum size.
The four built‑in rejection policies are:
ThreadPoolExecutor.AbortPolicy – discards the task and throws a RejectedExecutionException .
ThreadPoolExecutor.DiscardPolicy – silently discards the task.
ThreadPoolExecutor.DiscardOldestPolicy – discards the oldest task in the queue and retries the new one.
ThreadPoolExecutor.CallerRunsPolicy – the calling thread executes the rejected task.
The default policy is AbortPolicy . Inspecting the source of java.util.concurrent.ThreadPoolExecutor reveals:
/**
* The default rejected execution handler
*/
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();A simple program demonstrates this default behavior; when the queue is full and no custom handler is set, the executor throws a RejectedExecutionException .
public class ThreadPoolTest {
public static void main(String[] args) {
BlockingQueue
queue = new ArrayBlockingQueue<>(100);
ThreadFactory factory = r -> new Thread(r, "test-thread-pool");
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.SECONDS, queue, factory);
while (true) {
executor.submit(() -> {
try {
System.out.println(queue.size());
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
}To use a different policy, you can pass it to the constructor of ThreadPoolExecutor or, when using Spring, configure a ThreadPoolTaskExecutor :
@Configuration
public class TaskExecutorConfig implements AsyncConfigurer {
private static final int CORE_POOL_SIZE = 5;
private static final int MAX_POOL_SIZE = 5;
private static final int QUEUE_CAPACITY = 1000;
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
taskExecutor.setQueueCapacity(QUEUE_CAPACITY);
taskExecutor.initialize();
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
return taskExecutor;
}
}Scenario analysis:
AbortPolicy
Throws RejectedExecutionException . Suitable for critical business logic where immediate failure is preferable.
A handler for rejected tasks that throws a {@code RejectedExecutionException}.
DiscardPolicy
Silently drops the task. Useful for non‑essential background work such as page‑view counting.
A handler for rejected tasks that silently discards the rejected task.
DiscardOldestPolicy
Removes the oldest queued task and retries the new one. Choose this only when older tasks can be safely ignored.
A handler for rejected tasks that discards the oldest unhandled request and then retries {@code execute}, unless the executor is shut down.
CallerRunsPolicy
The calling thread runs the rejected task, providing a natural throttling mechanism. The following example shows the main thread executing tasks after the queue is saturated.
public static void main(String[] args) {
BlockingQueue
queue = new ArrayBlockingQueue<>(10);
ThreadFactory factory = r -> new Thread(r, "test-thread-pool");
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.SECONDS, queue, factory, new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < 1000; i++) {
executor.submit(() -> {
try {
System.out.println(Thread.currentThread().getName() + ": executing task");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}In the output you can see the main thread also processing tasks, confirming the behavior of CallerRunsPolicy .
In summary, the choice of rejection policy should be guided by the specific requirements and tolerance of your application for task loss or latency.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.