Engineering Core
ISB Vietnam's skilled software engineers deliver high-quality applications, leveraging their extensive experience in developing financial tools, business management systems, medical technology, and mobile/web platforms.
In Spring Boot applications, asynchronous processing helps increase system performance and responsiveness, especially when performing time-consuming tasks such as sending emails, logging, or executing external APIs.

Instead of waiting for a task to complete, we can let it run in parallel in the background, allowing the main thread to continue processing other requests.
SpringBoot supports this very simply through the @Async annotation.

 

I. How to enable asynchronous in Spring Boot

To use the asynchronous, you need to enable the asynchronous feature in your project using the @EnableAsync annotation

    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.annotation.EnableAsync;

    /**
     * The class AsyncConfig.
     */
    @Configuration
    @EnableAsync
    public class AsyncConfig {
    }

This annotation tells Spring Boot that you want to use an asynchronous mechanism in your application.

II. How to use the @Async

Assume that you have a service that sends emails.
If you send it synchronously, the request will have to wait for the email to be sent before responding — this is inefficient.
We can improve this by adding @Async:

    @Service
    public class EmailService {
        @Async
        public void sendEmail (String recipient) {
            System.out.println("Sending email to: "+ recipient);
            try {
                Thread.sleep(5000); // Simulate task takes much time
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("Finished sending email to: "+ recipient);
        }
    }

The controller can execute this service without waiting.

    @RestController
    @RequestMapping("/email")
    public class EmailController {
        private final EmailService emailService;


        public EmailController (EmailService emailService) {
            this.emailService= emailService;
        }


        @PostMapping("/send")
        public String send (@RequestParam String recipient) {
            emailService.sendEmail(recipient);
            return"Request processed!";
        }
    }

When
the user executes the "/email" API, the response is returned immediately while the email sending continues in the background thread.

 

III. Asynchronous processing with return values

If you need to get the result back from an asynchronous method, you can use CompletableFuture

    @Async
    public CompletableFuture<String>fetchUserData() throws InterruptedException {
        Thread.sleep(2000);
        return CompletableFuture.completedFuture("User data");
    }

You can use .get() or further process with .thenApply(), .thenAccept(), depending on your needs.
To merge multiple asynchronous tasks, follow the steps below.
Assume that you have two tasks that need to be executed in parallel, and only when both are completed should they continue — you can use CompletableFuture.allOf().

    @Async
    public CompletableFuture<String>getUserInfo() throws InterruptedException {
        Thread.sleep(2000);
        return CompletableFuture.completedFuture("User Info");
    }


    @Async
    public CompletableFuture<String>getOrderHistory() throws InterruptedException {
        Thread.sleep(3000);
        return CompletableFuture.completedFuture("Order History");
    }


    public StringmergeAsyncTasks() throws Exception {
        CompletableFuture<String> userInfo=getUserInfo();
        CompletableFuture<String> orderHistory=getOrderHistory();


        CompletableFuture.allOf(userInfo, orderHistory).join();


        return userInfo.get() +" | "+orderHistory.get();
    }

As you can see, when executing the mergeAsyncTasks(), both methods will execute at the same time.
Total processing time will be equal to the longest task only, instead of adding up each step.

IV. Important Note

@Async only works when executing from another bean; it doesn't execute internally in the same class.
You can customize ThreadPoolTaskExecutor to limit the number of threads, timeouts, or queue size.
@Async should only be used for tasks that do not require immediate results (like sending emails, writing logs, etc).

V. Summary

@Async is a powerful and easy-to-use tool in Spring Boot to handle asynchrony.
By simply adding @EnableAsync and setting @Async on the method that needs to run in the background, you can make your application more responsive, take advantage of multi-threading, and improve the user experience.

Whether you need scalable software solutions, expert IT outsourcing, or a long-term development partner, ISB Vietnam is here to deliver. Let’s build something great together—reach out to us today. Or click here to explore more ISB Vietnam's case studies.


[References]

  1. https://spring.io/guides/gs/async-method
  2. https://www.baeldung.com/spring-async
  3. https://medium.com/@bubu.tripathy/a-beginners-guide-to-async-processing-in-a-spring-boot-application-a4c785a992f2
  4. https://www.packtpub.com/en-us/learning/how-to-tutorials/solving-problems-with-spring-boot (Image source)
Written by
Author Avatar
Engineering Core
ISB Vietnam's skilled software engineers deliver high-quality applications, leveraging their extensive experience in developing financial tools, business management systems, medical technology, and mobile/web platforms.

COMPANY PROFILE

Please check out our Company Profile.

Download

COMPANY PORTFOLIO

Explore my work!

Download

ASK ISB Vietnam ABOUT DEVELOPMENT

Let's talk about your project!

Contact US