Spring Boot provides the @Async annotation to execute methods asynchronously. This improves application performance by freeing up the main thread while tasks execute in the background.
1. Why Use @Async?
By using @Async, you can:
- Improve application responsiveness.
- Execute long-running tasks in the background.
- Utilize multiple threads efficiently.
2. Enable Asynchronous Processing
Before using @Async, you need to enable it in your Spring Boot application. This can be done by adding @EnableAsync to your main class or any configuration class.
Example:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync // Enables asynchronous processing
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}
}
3. Using @Async in a Service
The @Async annotation can be applied to methods that should execute asynchronously.
Example:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class AsyncService {
private static final Logger logger = LoggerFactory.getLogger(AsyncService.class);
@Async
public void executeAsyncTask() {
logger.info("Executing task in thread: " + Thread.currentThread().getName());
try {
Thread.sleep(3000); // Simulating a long-running task
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
logger.info("Task completed");
}
}
4. Calling the Async Method
You need to call the method from another Spring-managed bean to ensure the async behavior works properly.
Example:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class AsyncRunner implements CommandLineRunner {
@Autowired
private AsyncService asyncService;
@Override
public void run(String... args) {
asyncService.executeAsyncTask();
System.out.println("Method call returned immediately");
}
}
Output:
Method call returned immediately
Executing task in thread: taskExecutor-1
Task completed
This shows that the method was executed asynchronously in a separate thread.
5. Returning a Value from an Async Method
You can return a CompletableFuture for better control of the async result.
Example:
import java.util.concurrent.CompletableFuture;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class AsyncService {
@Async
public CompletableFuture fetchData() {
try {
Thread.sleep(2000); // Simulating delay
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return CompletableFuture.completedFuture("Async Result");
}
}
Calling the Async Method:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
@Component
public class AsyncRunner implements CommandLineRunner {
@Autowired
private AsyncService asyncService;
@Override
public void run(String... args) throws Exception {
CompletableFuture future = asyncService.fetchData();
System.out.println("Fetching data...");
System.out.println("Result: " + future.join());
}
}
Output:
Fetching data...
Result: Async Result
6. Configuring Async Executor
By default, Spring uses SimpleAsyncTaskExecutor, but you can define a custom executor for better performance.
Example:
import java.util.concurrent.Executor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
executor.initialize();
return executor;
}
}
This configuration sets up a thread pool with custom properties.
Conclusion
The @Async annotation in Spring Boot helps execute tasks asynchronously, improving application performance. Using a custom executor ensures better resource management. CompletableFuture provides enhanced control over async results.
Related article: https://lotusteksolution.com/2025/04/02/java-multiple-threading-using-completablefuture-and-allof-method-a-comprehensive-guide-with-examples/