Spring Boot – @Async Annotation (Part 1)
1) What is a Thread Pool? (page 1)
From PDF:
- A collection of threads (workers) available to perform submitted tasks.
- After a task finishes, the thread returns to the pool and waits.
- Threads are reused.
- Java creates pools using ThreadPoolExecutor.
Code (page 1):
int minPoolSize = 2;
int maxPoolSize = 4;
int queueSize = 3;
ThreadPoolExecutor poolTaskExecutor =
new ThreadPoolExecutor(minPoolSize, maxPoolSize, 1,
TimeUnit.HOURS, new ArrayBlockingQueue<>(queueSize));
🧠 Extra Notes
- Thread pools avoid cost of creating/destroying threads repeatedly.
- Queue holds tasks when all core threads are busy.
- maxPoolSize is used only when queue is full.
2) What is @Async? (page 1)
From PDF:
- Used to mark methods that should run asynchronously.
- Runs in a new thread, without blocking the main thread.
Example (page 1):
@EnableAsync
@SpringBootApplication
public class SpringBootApplication { }
@Component
public class UserService {
@Async
public void asyncMethodTest() {
System.out.println("inside asyncMethodTest: " + Thread.currentThread().getName());
}
}
Output (page 1):
getUserMethodruns onhttp-nio-8080-exec-*asyncMethodTestruns ontask-*
Meaning:
Each call creates a new background thread.
3) Does @Async Always Use SimpleAsyncTaskExecutor? (page 2)
From PDF:
Many blogs say Spring Boot uses SimpleAsyncTaskExecutor by default.
➡️ Not fully correct.
Actual logic (page 2):
From AsyncExecutionInterceptor:
Executor defaultExecutor = super.getDefaultExecutor(beanFactory);
return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor());
✔️ Real behavior:
- Spring first looks for a default Executor bean.
- If found → uses it.
- Else → falls back to SimpleAsyncTaskExecutor.
4) Default Executor in Spring Boot (page 2)
If no ThreadPoolTaskExecutor bean is present:
Spring Boot auto-creates:
corePoolSize = 8
maxPoolSize = 2147483647
queueCapacity = 2147483647
keepAliveSeconds = 60
Note: ThreadPoolTaskExecutor is a Spring wrapper around Java’s ThreadPoolExecutor.
5) Why Default Config Is Dangerous (page 3)
From PDF:
- Underutilization
- Huge queue → tasks wait instead of creating threads.
- High Latency
- Requests pile up in queue.
- Thread Exhaustion
- maxPoolSize = Integer.MAX_VALUE → JVM crash possible.
- High Memory Usage
- Each thread consumes stack + heap → OOM risk.
6) Custom ThreadPoolTaskExecutor (page 3)
@Bean(name = "myThreadPoolExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(4);
executor.setQueueCapacity(3);
executor.setThreadNamePrefix("MyThread-");
executor.initialize();
return executor;
}
@Async("myThreadPoolExecutor")
public void asyncMethodTest() {
System.out.println(Thread.currentThread().getName());
}
Output: MyThread-1, MyThread-2 …
7) When Queue Gets Full (page 4)
Flow:
- Core threads used
- Queue fills
- New threads created until maxPoolSize
- Then → RejectedExecutionException
8) Why NOT SimpleAsyncTaskExecutor (page 4)
- Creates new thread every time
- No reuse
- Leads to:
- Thread exhaustion
- High overhead
- Memory leak risk
9) Making Your Executor the Default (page 5)
If you define ThreadPoolTaskExecutor (Java one):
@Bean
public Executor taskExecutor() { ... }
Then even:
@Async
public void asyncMethodTest() { }
➡️ Your executor is used automatically.
🎯 Interview Q&A
Q1. What is @Async?
Runs method in a separate thread without blocking caller.
Q2. Does @Async create a new thread always?
No. It uses an Executor. Only SimpleAsyncTaskExecutor creates new threads.
Q3. Default executor in Spring Boot?
ThreadPoolTaskExecutor (auto-configured). If not → SimpleAsyncTaskExecutor.
Q4. Why default config is risky?
Huge queue + unlimited threads → memory and performance issues.
Q5. How to control threads?
Define your own ThreadPoolTaskExecutor.
Q6. What happens when queue is full?
New threads are created till maxPoolSize, then request is rejected.
Q7. How to make your executor default?
Create a bean of type Executor without name.