This project demonstrates the implementation of ThreadPoolTaskExecutor in a Spring Boot application. It provides RESTful APIs to trigger asynchronous execution of tasks. The project includes two services:
- Order Service – Handles order processing asynchronously.
- Forgot Password Service – Manages forgot password requests asynchronously.
ThreadPoolTaskExecutor is a class provided by Spring Framework to handle asynchronous tasks in Spring. It is a powerful asynchronous task execution mechanism that allows you to manage a pool of worker threads efficiently. The ThreadPoolTaskExecutor is configured with various properties to optimize performance:
-
corePoolSize – The number of threads to keep in the pool, even if they are idle.
- If the number of threads is less than the core, a new thread is created to handle the task.
- If the number of threads is greater than the core, the task is added to the queue.
-
maxPoolSize – The maximum number of threads to allow in the pool.
- This limit prevents the thread pool from creating too many threads, which could overwhelm the system.
- When tasks are submitted, the thread pool starts with corePoolSize threads.
- If all corePoolSize threads are busy, new tasks are added to the queue.
- If the queue is full, additional threads are created up to maxPoolSize.
- Once maxPoolSize is reached, new tasks cannot be executed immediately and are handled based on the rejection policy.
-
queueCapacity – The maximum number of tasks that can be queued.
- It acts as a buffer that holds tasks waiting to be executed.
- If all corePoolSize threads are busy, the task goes into the queue (if there is space).
- If the number of tasks exceeds the queue capacity, additional threads are created up to maxPoolSize.
-
threadNamePrefix – The prefix to use for the names of the threads.
-
allowCoreThreadTimeout
- Whether to allow core threads to time out and terminate if no tasks arrive within the keepAliveSeconds time.
- By default, core threads are always kept alive, but enabling allowCoreThreadTimeOut(true) allows them to be removed when not needed.
- If allowCoreThreadTimeOut(false), core threads stay alive forever, even if idle.
- If allowCoreThreadTimeOut(true), core threads are removed if they remain idle for keepAliveSeconds.
- It only works if keepAliveSeconds is set.
- This can help reduce resource consumption when the number of tasks is low.
-
keepAliveSeconds
- When the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.
- Controls how long extra threads stay idle before being removed.
-
rejectedExecutionHandler – The handler to use when the executor cannot accept a task.
- There are four types of RejectedExecutionHandler:
- AbortPolicy: Throws a RejectedExecutionException.
- CallerRunsPolicy: Executes the task on the caller's thread.
- DiscardPolicy: Discards the task silently.
- DiscardOldestPolicy: Discards the oldest task in the queue and adds the new task.
- There are four types of RejectedExecutionHandler:
-
waitForTasksToCompleteOnShutdown
- Whether to wait for scheduled tasks to complete on shutdown.
-
awaitTerminationSeconds – The maximum time to wait for the executor to terminate.
@Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(corePoolSize); executor.setMaxPoolSize(maxPoolSize); executor.setQueueCapacity(queueCapacity); executor.setThreadNamePrefix(threadNamePrefix); executor.setAllowCoreThreadTimeOut(allowCoreThreadTimeout); executor.setKeepAliveSeconds(keepAliveSeconds); executor.setRejectedExecutionHandler(getRejectedExecutionHandler(rejectedExecutionHandler)); executor.setWaitForTasksToCompleteOnShutdown(waitForTasksToCompleteOnShutdown); executor.setAwaitTerminationSeconds(awaitTerminationSeconds); executor.initialize(); return executor; } }
The technology used in this project are:
Spring Boot Starter Web
– Provides essential components for building RESTful APIs.ThreadPoolTaskExecutor
– Manages a pool of worker threads efficiently for executing tasks asynchronously.Spring Retry
– Handles retry mechanisms for failed operations.
The project is organized into the following package structure:
async-executor/ │── src/main/java/com/yoanesber/spring/async_executor/ │ ├── 📂async/ # Defines task executor. │ ├── 📂config/ # Configures ThreadPoolTaskExecutor (corePoolSize, maxPoolSize, queueCapacity, etc.). │ ├── 📂controller/ # Contains REST controllers handling Forgot Password and Order Service requests. │ ├── 📂dto/ # Data Transfer Objects (DTOs) for request/response payloads. │ ├── 📂entity/ # Contains Order and OrderDetail classes representing order data. │ ├── 📂service/ # Business logic layer │ │ ├── 📂impl/ # Implementation of services
Configuration values are stored in .env.development
and referenced in application.properties
.
Example .env.development
file content:
# Application properties APP_PORT=8081 SPRING_PROFILES_ACTIVE=development # ThreadPoolTaskExecutor properties ASYNC_CORE_POOL_SIZE=5 ASYNC_MAX_POOL_SIZE=10 ASYNC_QUEUE_CAPACITY=100 ASYNC_THREAD_NAME_PREFIX=async- ASYNC_ALLOW_CORE_THREAD_TIMEOUT=true ASYNC_KEEP_ALIVE_SECONDS=10 ASYNC_REJECTED_EXECUTION_HANDLER=CALLER_RUNS ASYNC_WAIT_FOR_TASKS_TO_COMPLETE_ON_SHUTDOWN=true ASYNC_AWAIT_TERMINATION_SECONDS=10
Example application.properties
file content:
# Application properties spring.application.name=async-executor server.port=${APP_PORT} spring.profiles.active=${SPRING_PROFILES_ACTIVE} # ThreadPoolTaskExecutor properties async.executor.core.pool.size=${ASYNC_CORE_POOL_SIZE} async.executor.max.pool.size=${ASYNC_MAX_POOL_SIZE} async.executor.queue.capacity=${ASYNC_QUEUE_CAPACITY} async.executor.thread.name.prefix=${ASYNC_THREAD_NAME_PREFIX} async.executor.allow.core.thread.timeout=${ASYNC_ALLOW_CORE_THREAD_TIMEOUT} async.executor.keep.alive.seconds=${ASYNC_KEEP_ALIVE_SECONDS} async.executor.rejected.execution.handler=${ASYNC_REJECTED_EXECUTION_HANDLER} async.executor.wait.for.tasks.to.complete.on.shutdown=${ASYNC_WAIT_FOR_TASKS_TO_COMPLETE_ON_SHUTDOWN} async.executor.await.termination.seconds=${ASYNC_AWAIT_TERMINATION_SECONDS}
A step by step series of examples that tell you how to get a development env running.
- Ensure you have Git installed on your Windows machine, then clone the repository to your local environment:
git clone https://github.com/yoanesber/Spring-Boot-ThreadPoolTaskExecutor.git cd Spring-Boot-ThreadPoolTaskExecutor
- Build and run the application
mvn spring-boot:run
POST
http://localhost:8081/api/v1/order/process/{orderId} - Process order asynchronously.
Successful Response:
Order processed successfully
POST
http://localhost:8081/api/v1/password/forgot-password - Initiate forgot password request (async processing).
Request Body:
{ "email": "jhonny@myemail.com" }
Successful Response:
Password reset email sent successfully
- Scheduled Tasks using ThreadPoolTaskScheduler GitHub Repository, check out Spring Boot Scheduled Tasks with ThreadPoolTaskScheduler.