Mastering Asynchronous Calls in Spring Boot: From Sync to Async with @Async

This article explains the difference between synchronous and asynchronous calls, demonstrates a synchronous Task example, shows how to convert it to asynchronous execution with Spring Boot's @Async annotation, and introduces Future-based callbacks to coordinate concurrent tasks and measure total execution time.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
Mastering Asynchronous Calls in Spring Boot: From Sync to Async with @Async

Asynchronous calls are a universal solution for high‑concurrency web‑application performance issues. Synchronous calls execute sequentially, waiting for each method to finish; asynchronous calls proceed without waiting for the result.

“Asynchronous call” corresponds to “synchronous call”. Synchronous call means the program follows the defined order, each line must wait for the previous one to finish; asynchronous call means the program does not wait for the asynchronous statement’s result before executing the next statements.

Synchronous Call Example

A Task class defines three methods— doTaskOne, doTaskTwo, doTaskThree —each sleeps for a random time (up to 10 seconds) and prints start and completion messages.

@Component
public class Task {
    public static Random random = new Random();
    public void doTaskOne() throws Exception {
        System.out.println("开始做任务一");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
    }
    public void doTaskTwo() throws Exception {
        System.out.println("开始做任务二");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
    }
    public void doTaskThree() throws Exception {
        System.out.println("开始做任务三");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
    }
}

A unit test injects Task and calls the three methods sequentially, producing ordered output such as:

开始做任务一 … 完成任务一,耗时:4256毫秒

开始做任务二 … 完成任务二,耗时:4957毫秒

开始做任务三 … 完成任务三,耗时:7173毫秒

Asynchronous Call

By adding the @Async annotation to each method and enabling asynchronous processing in the Spring Boot application with @EnableAsync, the three tasks can run concurrently.

@Component
public class Task {
    @Async
    public void doTaskOne() throws Exception { /* same body as before */ }
    @Async
    public void doTaskTwo() throws Exception { /* same body as before */ }
    @Async
    public void doTaskThree() throws Exception { /* same body as before */ }
}
@SpringBootApplication
@EnableAsync
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Running the same unit test may produce no output, partial output, or out‑of‑order output because the main thread may terminate before the asynchronous tasks finish.

Note: Methods annotated with @Async must not be declared static , otherwise the asynchronous behavior will not take effect.

Asynchronous Callback with Future

To know when all async tasks have completed, modify the methods to return Future<String> and wrap the result with AsyncResult:

@Async
public Future<String> doTaskOne() throws Exception {
    System.out.println("开始做任务一");
    long start = System.currentTimeMillis();
    Thread.sleep(random.nextInt(10000));
    long end = System.currentTimeMillis();
    System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
    return new AsyncResult<>("任务一完成");
}

The test captures the Future objects, polls isDone() in a loop, and calculates total elapsed time after all tasks finish:

@Test
public void test() throws Exception {
    long start = System.currentTimeMillis();
    Future<String> task1 = task.doTaskOne();
    Future<String> task2 = task.doTaskTwo();
    Future<String> task3 = task.doTaskThree();
    while (true) {
        if (task1.isDone() && task2.isDone() && task3.isDone()) {
            break;
        }
        Thread.sleep(1000);
    }
    long end = System.currentTimeMillis();
    System.out.println("任务全部完成,总耗时:" + (end - start) + "毫秒");
}

The final output shows that concurrent execution reduces the total runtime (e.g., total 8025 ms) compared with the sequential version.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaconcurrencySpring BootAsyncFutureAsync
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.