Mastering Java ThreadPoolExecutor: 4 Rejection Policies Explained & When to Use Them

Java's ThreadPoolExecutor offers four rejection policies—AbortPolicy, DiscardPolicy, DiscardOldestPolicy, and CallerRunsPolicy—each handling task overflow differently; this article explains their behavior, default setting, code examples, and how to configure them via Spring's ThreadPoolTaskExecutor for various business scenarios.

Java Interview Crash Guide
Java Interview Crash Guide
Java Interview Crash Guide
Mastering Java ThreadPoolExecutor: 4 Rejection Policies Explained & When to Use Them

Preface

Thread pools are widely used, but many developers are unfamiliar with their rejection policies.

Four ThreadPoolExecutor Rejection Policies

When the task queue is full and the number of threads reaches maximumPoolSize, new tasks are handled by one of the following policies:

ThreadPoolExecutor.AbortPolicy – discards the task and throws a RejectedExecutionException.

ThreadPoolExecutor.DiscardPolicy – silently discards the task without an exception.

ThreadPoolExecutor.DiscardOldestPolicy – removes the oldest task in the queue and retries the rejected task.

ThreadPoolExecutor.CallerRunsPolicy – the calling thread (the thread that submitted the task) executes the task.

Default Rejection Policy

Inspecting the source of java.util.concurrent.ThreadPoolExecutor shows that the default handler is an AbortPolicy:

/**
 * The default rejected execution handler
 */
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();

Thus, by default the pool discards the task and throws a RejectedExecutionException. The following test code demonstrates this behavior:

public class ThreadPoolTest {
    public static void main(String[] args) {
        BlockingQueue<Runnable> 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();
                }
            });
        }
    }
}

Running the program confirms that the default policy is AbortPolicy, which throws RejectedExecutionException when the queue is saturated.

Configuring a Custom Rejection Policy

Developers can set a different policy via the overloaded constructor of ThreadPoolExecutor or, when using Spring, through 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;
    }
}

By calling setRejectedExecutionHandler on the Spring ThreadPoolTaskExecutor, any of the four policies can be applied.

Policy Scenario Analysis

AbortPolicy

Throws RejectedExecutionException. It is the default and is suitable for critical business logic where immediate failure notification is required.

A handler for rejected tasks that throws a {@code RejectedExecutionException}.

DiscardPolicy

Silently discards the task. Use it for non‑essential work where losing tasks is acceptable.

A handler for rejected tasks that silently discards the rejected task.

DiscardOldestPolicy

Removes the oldest queued task and retries the new one. Choose this when older tasks can be safely dropped in favor of newer ones.

A handler for rejected tasks that discards the oldest unhandled request and then retries {@code execute}, unless the executor is shut down.

CallerRunsPolicy

The submitting thread runs the task itself. This throttles the submission rate and provides a simple back‑pressure mechanism.

A handler for rejected tasks that runs the rejected task directly in the calling thread of the {@code execute} method, unless the executor has been shut down.

Example demonstrating CallerRunsPolicy:

public static void main(String[] args) {
    BlockingQueue<Runnable> 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();
            }
        });
    }
}

When the queue is full, the main thread also executes tasks, confirming the behavior of CallerRunsPolicy.

Conclusion

The article introduced the four ThreadPoolExecutor rejection policies and demonstrated how to inspect the default, configure custom handlers, and choose the appropriate policy based on specific business requirements.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaconcurrencyspringThreadPoolExecutorRejectionPolicy
Java Interview Crash Guide
Written by

Java Interview Crash Guide

Dedicated to sharing Java interview Q&A; follow and reply "java" to receive a free premium Java interview guide.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.