Using @Async in Spring Boot for Asynchronous Processing

1 min read

Using @Async in Spring Boot for Asynchronous Processing

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/

🤞 Never miss a story from us, get weekly updates to your inbox!