Clean • Professional
@AsyncAs modern applications scale, running long tasks synchronously can slow down APIs and degrade user experience. Tasks like sending emails, generating reports, processing files, or calling third-party APIs should not block the main request thread.
That’s where @Async in Spring Boot becomes powerful. It allows methods to run in a separate thread without manually managing thread creation.
@Async?@Async is a Spring annotation that allows a method to run in a separate thread, enabling asynchronous processing without blocking the main request thread.
Without @Async (Synchronous Execution)
Client → Controller → Service → Long Task → Response (waits)
With @Async (Asynchronous Execution)
Client → Controller → Service (@Async)
↳ Background Thread Executes Task Response returned immediately
To activate asynchronous support in Spring Boot, add @EnableAsync to your main application class:
@EnableAsync
@SpringBootApplication
public class Application {
}
Without @EnableAsync, Spring will not activate async support.
@Async ExampleHere’s a simple example of running a method asynchronously:
@Service
public class EmailService {
@Async
public void sendEmail(String to) {
System.out.println("Sending email...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
System.out.println("Email sent to " + to);
}
}
Calling the method:
emailService.sendEmail("[email protected]");
The sendEmail() method runs in a separate background thread, so the main request thread does not wait for it to finish.
@AsyncIf you need to return a result from an asynchronous method, use CompletableFuture:
@Async
public CompletableFuture<String> processOrder() {
return CompletableFuture.completedFuture("Order Processed");
}
Calling the method:
CompletableFuture<String> result = orderService.processOrder();
System.out.println(result.get());
Spring supports the following return types for async methods:
FutureCompletableFutureListenableFuture@Async Works InternallySpring executes @Async methods using proxy-based AOP and a TaskExecutor (thread pool) behind the scenes.
@Async method.TaskExecutor).Execution Flow
Client → Spring Proxy → Task Submitted to Thread Pool → Executes in Background
Important:
@Async methods.public methods are supported.By default, Spring uses a simple executor, which is not ideal for high-traffic production systems. For better performance and stability, always configure a custom thread pool.
Custom Async Configuration
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5); // Minimum threads always active
executor.setMaxPoolSize(10); // Maximum threads allowed
executor.setQueueCapacity(50); // Queue size before creating new threads
executor.setThreadNamePrefix("Async-");
executor.initialize();
return executor;
}
}
Use specific executor:
@Async("taskExecutor")
public void sendEmail(String to) {
// async logic here
}
Why configure it?
In Spring Boot, exceptions thrown inside @Async methods behave differently depending on the return type.
void, exceptions are not automatically propagated to the caller.Future or CompletableFuture, exceptions can be handled using .get() or .exceptionally().For void async methods, you should implement a custom exception handler.
Implement a custom handler:
Step 1: Create a Custom Async Exception Handler
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import java.lang.reflect.Method;
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
System.out.println("Async error in method: " + method.getName());
System.out.println("Error message: " + ex.getMessage());
}
}
This handler captures uncaught exceptions from async methods and allows you to log or monitor them properly.
Step 2: Register the Exception Handler
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import java.util.concurrent.Executor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
return new ThreadPoolTaskExecutor();
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}
In modern Spring Boot applications, @Async is commonly used to handle background tasks that should not block the main request thread.
@Async is ideal for:
Practical Example
When a user registers:
User → Registration API → Response returned immediately
↳ Welcome email sent asynchronously
The API responds instantly to the user, while the email is processed in the background.
@EnableAsync@Async vs Messaging Systems| Feature | @Async (Spring) | Messaging Systems (Kafka / RabbitMQ) |
|---|---|---|
| Scope | Works inside a single application | Works across multiple services |
| Architecture | Thread-based asynchronous execution | Event-driven, message-based communication |
| Use Case | Lightweight background tasks | Distributed and cross-service workflows |
| Delivery Guarantee | No built-in guarantee | Supports guaranteed delivery |
| Retry Mechanism | Manual handling required | Built-in retry and acknowledgment support |
| Scalability | Limited to application thread pool | Highly scalable across clusters |
| Durability | No persistence by default | Persistent message storage supported |
| Best For | Emails, logging, small async tasks | Microservices communication, event streaming |
@Async in the Spring Framework is a clean and powerful way to improve API responsiveness by offloading long-running tasks to background threads.
When used correctly with:
It becomes a production-ready solution for handling lightweight asynchronous operations efficiently.