๐Ÿ”ด The Error You're Seeing

Confirm this matches your console output. If it does, you're in the right place.

ERROR LOG2026-02-20 12:15:30.812 ERROR 8842 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask rejected from java.util.concurrent.ThreadPoolExecutor] with root cause java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask rejected from java.util.concurrent.ThreadPoolExecutor[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]

โšก Quick Fix Works 80% of the time

Increase the queueCapacity of your ThreadPoolTaskExecutor.

@Bean public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setQueueCapacity(500); // Explicit bounded queue (default is unbounded Integer.MAX_VALUE) return executor; }

๐Ÿง  Why this Happens

Tap to expand the deep technical explanation

You submitted a background task (e.g., via `@Async`), but the thread pool's core threads are all busy, and the internal queue holding pending tasks is completely full. The thread pool's RejectedExecutionHandler stepped in and rejected the task to prevent the system from running out of memory.

The HITEC City Parking Spot Analogy:

It's like a restaurant kitchen. All the chefs (core threads) are busy cooking, and the counter holding order tickets (queue) is completely full. The waiter trying to put down a new ticket is told 'We cannot accept any more orders right now.'

๐Ÿ” How to Reproduce Confirm this is your error

Create a `ThreadPoolTaskExecutor` with `setCorePoolSize(1)` and `setQueueCapacity(1)`. Trigger two `@Async` methods simultaneously. The second method will be rejected because the 1 thread is busy and the 1-slot queue is full.

๐Ÿ› ๏ธ Solutions (5 Ways to Fix)

Solution 1โœ“ Most common cause

Increase queue capacity

๐Ÿ‘‰ Use this if you experience sudden bursts of traffic but can process them eventually.

A larger queue allows more tasks to wait patiently for a thread to become available.

@Bean public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(100); // Hold up to 100 pending tasks executor.initialize(); return executor; }
Solution 2

Increase max pool size

๐Ÿ‘‰ Use this if your tasks are CPU intensive and you have server capacity.

Allow the pool to spawn new threads when the queue is full, up to the max pool size.

executor.setMaxPoolSize(50); // Spawn up to 50 threads before rejecting
Solution 3

Change the Rejection Policy

๐Ÿ‘‰ Use this if you want the calling thread to run the task instead of rejecting it.

Set the RejectedExecutionHandler to `CallerRunsPolicy`. The Tomcat HTTP thread will execute the async task itself, slowing down the client but preventing data loss.

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
Solution 4

Fix thread leaks / infinite loops

๐Ÿ‘‰ Use this if threads are never returning to the pool.

If your @Async method has an infinite loop or a very long blocking call, threads get stuck. Ensure tasks eventually complete.

@Async public void process() { // Ensure there is no while(true) blocking forever // and that external API calls have timeouts. }
Solution 5

Do not shut down the executor prematurely

๐Ÿ‘‰ Use this if the error shows 'Terminated, pool size = 0'.

If you call `executor.shutdown()` somewhere in your code, the pool stops accepting tasks. Let Spring manage the lifecycle.

// Remove any manual calls to executor.shutdown();

๐Ÿ“‹ Version Notes

Spring Boot 2.x

Default async executor uses SimpleAsyncTaskExecutor (no pooling).

Spring Boot 3.x

Encourages explicit ThreadPoolTaskExecutor configuration.

๐Ÿ›ก๏ธ How to Prevent This Next Time

Monitor your thread pools in production using Spring Boot Actuator metrics. Right-size your pools based on your task duration and traffic.

Course Search
Search across all chapters & stages
๐Ÿ“–

Search the course

Type any topic โ€” branching, stash, rebase, hooks โ€” and jump straight to that chapter.

merge branchesgit stashundo commitrebase