Mastering Java ThreadPoolExecutor Rejection Policies: When Tasks Get Dropped
This guide explains the five ThreadPoolExecutor rejection policies in Java—AbortPolicy, CallerRunsPolicy, DiscardPolicy, DiscardOldestPolicy, and custom handlers—detailing their behavior, use‑cases, and providing concrete code examples for each.
Java's ThreadPoolExecutor uses rejection policies to decide what happens when the pool and its queue are full. Understanding these policies helps you choose the right strategy for your application's reliability and performance.
1. AbortPolicy (default)
When the pool cannot accept new tasks, it throws a RejectedExecutionException, immediately rejecting the task.
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1, 1, 0, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1),
new ThreadPoolExecutor.AbortPolicy());
executor.submit(() -> System.out.println("Task 1"));
executor.submit(() -> System.out.println("Task 2")); // RejectedThis policy notifies the caller that the executor is saturated.
2. CallerRunsPolicy
If the pool is full, the task is executed in the thread that submitted it, avoiding immediate rejection but potentially slowing the caller.
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1, 1, 0, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1),
new ThreadPoolExecutor.CallerRunsPolicy());
executor.submit(() -> System.out.println("Task 1"));
executor.submit(() -> {
System.out.println("Task 2 (CallerRunsPolicy)");
System.out.println("Executed by: " + Thread.currentThread().getName());
});Use this policy when you can tolerate the caller thread doing extra work.
3. DiscardPolicy
New tasks are silently dropped without any exception or execution.
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1, 1, 0, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1),
new ThreadPoolExecutor.DiscardPolicy());
executor.submit(() -> System.out.println("Task 1"));
executor.submit(() -> System.out.println("Task 2")); // DiscardedSuitable for low‑priority work where occasional loss is acceptable, such as logging or metrics collection.
4. DiscardOldestPolicy
The oldest task in the queue is removed to make room for the new task.
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1, 1, 0, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1),
new ThreadPoolExecutor.DiscardOldestPolicy());
executor.submit(() -> System.out.println("Task 1"));
executor.submit(() -> System.out.println("Task 2")); // Task 1 discarded, Task 2 queuedUseful when newer tasks have higher priority than older ones.
5. Custom Rejection Policy
You can implement RejectedExecutionHandler to define any behavior you need.
class CustomRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("Custom Rejected Execution: " + r.toString());
}
}
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1, 1, 0, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1),
new CustomRejectedExecutionHandler());
executor.submit(() -> System.out.println("Task 1"));
executor.submit(() -> System.out.println("Task 2")); // Triggers custom handlerChoose the policy that best matches your application's tolerance for task loss, latency, and caller impact.
Architect Chen
Sharing over a decade of architecture experience from Baidu, Alibaba, and Tencent.
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.
