4

What is "asynchronous call"? "Asynchronous call" corresponds to "synchronous call". Synchronous call means that the program is executed in sequence according to the defined order, and each line of the program must wait for the execution of the previous line of program to complete before being executed; asynchronous call means that the program is executed in sequence without waiting for the asynchronous call The following program is executed when the statement returns the result.

Synchronous call

Let's use a simple example to intuitively understand what is a synchronous call:

Define the Task class, create three processing functions to simulate the operations of three tasks respectively, and the operation time is randomly selected (within 10 seconds)

@Slf4j
@Component
public class AsyncTasks {

    public static Random random = new Random();

    public void doTaskOne() throws Exception {
        log.info("开始做任务一");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务一,耗时:" + (end - start) + "毫秒");
    }

    public void doTaskTwo() throws Exception {
        log.info("开始做任务二");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务二,耗时:" + (end - start) + "毫秒");
    }

    public void doTaskThree() throws Exception {
        log.info("开始做任务三");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务三,耗时:" + (end - start) + "毫秒");
    }

}

In the unit test case, inject the Task object, and execute the three functions doTaskOne , doTaskTwo , and doTaskThree

@Slf4j
@SpringBootTest
public class Chapter75ApplicationTests {

    @Autowired
    private AsyncTasks asyncTasks;

    @Test
    public void test() throws Exception {
        asyncTasks.doTaskOne();
        asyncTasks.doTaskTwo();
        asyncTasks.doTaskThree();
    }

}

Perform unit test, you can see output similar to the following:

2021-09-11 23:19:12.922  INFO 92539 --- [           main] com.didispace.chapter75.AsyncTasks       : 开始做任务一
2021-09-11 23:19:17.788  INFO 92539 --- [           main] com.didispace.chapter75.AsyncTasks       : 完成任务一,耗时:4865毫秒
2021-09-11 23:19:17.788  INFO 92539 --- [           main] com.didispace.chapter75.AsyncTasks       : 开始做任务二
2021-09-11 23:19:24.851  INFO 92539 --- [           main] com.didispace.chapter75.AsyncTasks       : 完成任务二,耗时:7063毫秒
2021-09-11 23:19:24.851  INFO 92539 --- [           main] com.didispace.chapter75.AsyncTasks       : 开始做任务三
2021-09-11 23:19:26.928  INFO 92539 --- [           main] com.didispace.chapter75.AsyncTasks       : 完成任务三,耗时:2076毫秒

The execution of task one, task two, and task three in sequence is completed, in other words, the three functions of doTaskOne, doTaskTwo, and doTaskThree are executed in sequence.

Asynchronous call

Although the above-mentioned synchronous call successfully executed the three tasks, it can be seen that the execution time is relatively long. If there is no dependency between the three tasks themselves and can be executed concurrently, the synchronous call is relatively poor in terms of execution efficiency. You can consider concurrent execution through asynchronous calls.

In Spring Boot, we only need to use the @Async annotation to easily change the original synchronous function into an asynchronous function. The Task class is changed to the following mode:

@Slf4j
@Component
public class AsyncTasks {

    public static Random random = new Random();

    @Async
    public void doTaskOne() throws Exception {
        log.info("开始做任务一");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务一,耗时:" + (end - start) + "毫秒");
    }

    @Async
    public void doTaskTwo() throws Exception {
        log.info("开始做任务二");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务二,耗时:" + (end - start) + "毫秒");
    }

    @Async
    public void doTaskThree() throws Exception {
        log.info("开始做任务三");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务三,耗时:" + (end - start) + "毫秒");
    }

}

In order for the @Async annotation to take effect, you also need to configure @EnableAsync in the main Spring Boot program, as shown below:

@EnableAsync
@SpringBootApplication
public class Chapter75Application {

    public static void main(String[] args) {
        SpringApplication.run(Chapter75Application.class, args);
    }

}

At this point, you can execute the unit test repeatedly, and you may encounter various results, such as:

  • No task related output
  • Some task-related output
  • Out-of-order task-related output
  • The reason is that the doTaskOne , doTaskTwo doTaskThree are already executed asynchronously. After the main program is called asynchronously, the main program does not care whether the execution of these three functions is completed. Since there is no other content that needs to be executed, the program ends automatically, resulting in incomplete or no output task-related content. .

Note: @Async should not be defined as static type, so the asynchronous call will not take effect

Asynchronous callback

In order for doTaskOne , doTaskTwo , doTaskThree to finish normally, suppose we need to count how much time is spent in concurrent execution of the three tasks. This needs to wait until the above three functions are all mobilized and then record the time and calculate the result.

So how do we judge whether the above three asynchronous calls have been executed? We need to use CompletableFuture<T> to return the result of the asynchronous call, just like the following way to transform the doTaskOne function:

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

After transforming the other two asynchronous functions in the above way, let's transform the test case so that the test can do some other things after waiting for the completion of the three asynchronous calls.

@Test
public void test() throws Exception {
    long start = System.currentTimeMillis();

    CompletableFuture<String> task1 = asyncTasks.doTaskOne();
    CompletableFuture<String> task2 = asyncTasks.doTaskTwo();
    CompletableFuture<String> task3 = asyncTasks.doTaskThree();

    CompletableFuture.allOf(task1, task2, task3).join();

    long end = System.currentTimeMillis();

    log.info("任务全部完成,总耗时:" + (end - start) + "毫秒");
}

See what changes we made:

  • Record the start time at the beginning of the test case
  • When calling three asynchronous functions, return a result object of type CompletableFuture<String>
  • Through CompletableFuture.allOf(task1, task2, task3).join() achieve the blocking effect before the end of the three asynchronous tasks
  • After the three tasks are completed, the total time for concurrent execution of the three tasks is calculated based on the end time-the start time.

Perform the above unit test, you can see the following results:

2021-09-11 23:33:38.842  INFO 95891 --- [         task-3] com.didispace.chapter75.AsyncTasks       : 开始做任务三
2021-09-11 23:33:38.842  INFO 95891 --- [         task-2] com.didispace.chapter75.AsyncTasks       : 开始做任务二
2021-09-11 23:33:38.842  INFO 95891 --- [         task-1] com.didispace.chapter75.AsyncTasks       : 开始做任务一
2021-09-11 23:33:45.155  INFO 95891 --- [         task-2] com.didispace.chapter75.AsyncTasks       : 完成任务二,耗时:6312毫秒
2021-09-11 23:33:47.308  INFO 95891 --- [         task-3] com.didispace.chapter75.AsyncTasks       : 完成任务三,耗时:8465毫秒
2021-09-11 23:33:47.403  INFO 95891 --- [         task-1] com.didispace.chapter75.AsyncTasks       : 完成任务一,耗时:8560毫秒
2021-09-11 23:33:47.404  INFO 95891 --- [           main] c.d.chapter75.Chapter75ApplicationTests  : 任务全部完成,总耗时:8590毫秒

It can be seen that through asynchronous calls, tasks one, two, and three are executed concurrently, which effectively reduces the total running time of the program. This series of tutorials "Spring Boot 2.x Basic Tutorial" click directly! , welcome to collect and forward! What if you encounter difficulties in the learning process? You can join our Spring technical exchange group , participate in exchanges and discussions, and learn and progress better!

Code example

The complete works of this paper can be viewed below warehouse 2.x directory chapter7-5 project:

If you think this article is good, welcome Star support, your attention is my motivation for persistence!

Welcome to pay attention to my public account: Program Ape DD, share knowledge and thoughts that can’t be seen elsewhere

程序猿DD
2.2k 声望2.8k 粉丝

作品:《Spring Cloud微服务实战》、SpringForAll社区、OpenWrite、Youtube中文配音