What Happens When a Spring Boot ThreadPool Overflows? Learn Rejection Strategies
This article demonstrates how a Spring Boot thread pool behaves when its core size, max size, and queue capacity are exceeded by multiple asynchronous tasks, explains the resulting TaskRejectedException, and shows how to configure built‑in or custom rejection policies to handle such scenarios.
Through the previous three posts on implementing Spring Boot asynchronous tasks, we learned how to create async tasks with @Async, configure thread pools, and isolate different async tasks using multiple thread pools.
Now we explore what happens when a thread pool is configured with core size 2, max size 2, queue capacity 2, and five tasks are submitted simultaneously.
Scenario Reproduction
First, create a Spring Boot application and define the thread pool configuration as described.
@EnableAsync
@SpringBootApplication
public class Chapter78Application {
public static void main(String[] args) {
SpringApplication.run(Chapter78Application.class, args);
}
@Configuration
class TaskPoolConfig {
@Bean
public Executor taskExecutor1() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(2);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("executor-1-");
return executor;
}
}
}Second, use the @Async annotation to implement a partial task.
@Slf4j
@Component
public class AsyncTasks {
public static Random random = new Random();
@Async("taskExecutor1")
public CompletableFuture<String> doTaskOne(String taskNo) throws Exception {
log.info("开始任务:{}", taskNo);
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
log.info("完成任务:{},耗时:{} 毫秒", taskNo, end - start);
return CompletableFuture.completedFuture("任务完成");
}
}Third, write a test case that submits five tasks at once.
@Slf4j
@SpringBootTest
public class Chapter78ApplicationTests {
@Autowired
private AsyncTasks asyncTasks;
@Test
public void test2() throws Exception {
// ThreadPool: core=2, max=2, queue=2, 5 tasks -> exception
long start = System.currentTimeMillis();
CompletableFuture<String> task1 = asyncTasks.doTaskOne("1");
CompletableFuture<String> task2 = asyncTasks.doTaskOne("2");
CompletableFuture<String> task3 = asyncTasks.doTaskOne("3");
CompletableFuture<String> task4 = asyncTasks.doTaskOne("4");
CompletableFuture<String> task5 = asyncTasks.doTaskOne("5");
CompletableFuture.allOf(task1, task2, task3, task4, task5).join();
long end = System.currentTimeMillis();
log.info("任务全部完成,总耗时:" + (end - start) + "毫秒");
}
}Running the test produces logs similar to:
2021-09-22 17:33:08.159 INFO 21119 --- [ executor-1-2] com.didispace.chapter78.AsyncTasks : 开始任务:2
2021-09-22 17:33:08.159 INFO 21119 --- [ executor-1-1] com.didispace.chapter78.AsyncTasks : 开始任务:1
org.springframework.core.task.TaskRejectedException: Executor [java.util.concurrent.ThreadPoolExecutor@3e1a3801[Running, pool size = 2, active threads = 2, queued tasks = 2, completed tasks = 0]] did not accept task: java.util.concurrent.CompletableFuture$AsyncSupply@64968732
...The exception indicates that the fifth task is rejected because the thread pool and its queue are full.
Configuring Rejection Policies
Spring's ThreadPoolTaskExecutor allows you to set a custom rejection handler via setRejectedExecutionHandler. The JDK provides four built‑in policies:
AbortPolicy – default; throws RejectedExecutionException when the queue is full.
DiscardPolicy – silently discards the rejected task.
DiscardOldestPolicy – discards the oldest queued task and tries to enqueue the new one.
CallerRunsPolicy – the calling thread runs the task itself.
Example of setting a policy:
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// other configurations
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());To define a custom policy, implement RejectedExecutionHandler:
executor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// custom logic
}
});Or using a lambda expression:
executor.setRejectedExecutionHandler((r, exec) -> {
// custom logic
});With these configurations you can tailor the thread‑pool behavior to match specific business requirements.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
