What is “asynchronous invocation”? “Asynchronous invocation” corresponds to “synchronous invocation”. Synchronous invocation means that programs are executed in a defined order. Each line of programs must wait for the completion of the previous line of programs to be executed. Asynchronous invocation refers to the sequential execution of a program without waiting for the result of the asynchronous invocation.

A synchronous invocation

Here is a simple example to get an intuitive understanding of what synchronous calls are:

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

@Slf4j
@Component
public class AsyncTasks {

    public static Random random = new Random();

    public void doTaskOne(a) throws Exception {
        log.info("Start on task one.");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("Complete Task 1, Time:" + (end - start) + "毫秒");
    }

    public void doTaskTwo(a) throws Exception {
        log.info("Start on task two.");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("Complete Task 2, Time:" + (end - start) + "毫秒");
    }

    public void doTaskThree(a) throws Exception {
        log.info("Start on task three.");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("Complete Task 3, Time:" + (end - start) + "毫秒"); }}Copy the code

In the unit test case, the Task object is injected and the three functions doTaskOne, doTaskTwo, and doTaskThree are executed in the test case.

@Slf4j
@SpringBootTest
public class Chapter75ApplicationTests {

    @Autowired
    private AsyncTasks asyncTasks;

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

When you execute the unit test, you see output similar to the following:

The 2021-09-11 23:19:12. 92539-922 the INFO [main] com. Didispace. Chapter75. AsyncTasks: Start to do task a 2021-09-11 23:19:17. 788 INFO 92539 - [the main] com. Didispace. Chapter75. AsyncTasks: To complete the task, a time-consuming: 4865 milliseconds 23:19:17 2021-09-11. 92539-788 INFO. [the main] com didispace. Chapter75. AsyncTasks: Start doing task 2 2021-09-11 23:19:24. 851 INFO 92539 - [the main] com. Didispace. Chapter75. AsyncTasks: To complete the task two, time-consuming: 7063 milliseconds 23:19:24 2021-09-11. 92539-851 the INFO [main] com. Didispace. Chapter75. AsyncTasks: Start doing task three 23:19:26 2021-09-11. 928 INFO 92539 - [the main] com. Didispace. Chapter75. AsyncTasks: to complete the task 3, time-consuming: 2076 millisecondsCopy the code

DoTaskOne, doTaskTwo, and doTaskThree have been executed sequentially.

The asynchronous call

Although the above synchronous invocation 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 and they can be executed concurrently, the execution efficiency of synchronous invocation is relatively poor. Therefore, asynchronous invocation can be considered for concurrent execution.

In Spring Boot, we can simply change a synchronous function to an asynchronous function by using the @async annotation, and change the Task class to the following mode:

@Slf4j
@Component
public class AsyncTasks {

    public static Random random = new Random();

    @Async
    public void doTaskOne(a) throws Exception {
        log.info("Start on task one.");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("Complete Task 1, Time:" + (end - start) + "毫秒");
    }

    @Async
    public void doTaskTwo(a) throws Exception {
        log.info("Start on task two.");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("Complete Task 2, Time:" + (end - start) + "毫秒");
    }

    @Async
    public void doTaskThree(a) throws Exception {
        log.info("Start on task three.");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("Complete Task 3, Time:" + (end - start) + "毫秒"); }}Copy the code

In order for the @async annotation to work, you also need to configure @enableAsync in the Spring Boot main program, as follows:

@EnableAsync
@SpringBootApplication
public class Chapter75Application {

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

At this point, you can run unit tests repeatedly, and you may encounter different results, such as:

  • There is no task-related output

  • There are partial task-specific outputs

  • Out-of-order task-related output

  • The reason is that the doTaskOne, doTaskTwo, and doTaskThree functions are executed asynchronously. After the main program is called asynchronously, the main program does not care whether the execution of these three functions is complete. Because there is no other content to be executed, the program will automatically end, resulting in incomplete or no output task related content.

Note: Do not define @async functions as static, so asynchronous calls do not take effect

An asynchronous callback

In order for doTaskOne, doTaskTwo, and doTaskThree to complete properly, suppose we need to count the total time required for the concurrent execution of the three tasks. We need to record the time and calculate the result after all the three functions are completed.

So how do we determine if the above three asynchronous calls have completed? We need to use CompletableFuture

to return the result of the asynchronous call, like modifying the doTaskOne function as follows:

    @Async
public CompletableFuture<String> doTaskOne(a) throws Exception {
    log.info("Start on task one.");
    long start = System.currentTimeMillis();
    Thread.sleep(random.nextInt(10000));
    long end = System.currentTimeMillis();
    log.info("Complete Task 1, Time:" + (end - start) + "毫秒");
    return CompletableFuture.completedFuture("Mission one complete.");
}
Copy the code

After modifying the other two asynchronous functions as described above, let’s modify the test case so that the test does something else after waiting for three asynchronous calls.

@Test
public void test(a) 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("All tasks completed, total time:" + (end - start) + "毫秒");
}
Copy the code

See what changes we’ve made:

  • Record the start time at the beginning of the test case
  • Returns when three asynchronous functions are calledCompletableFuture<String>Type
  • throughCompletableFuture.allOf(task1, task2, task3).join()Implement blocking until all three asynchronous tasks are finished
  • After all three tasks are complete, calculate the total time required for the concurrent execution of the three tasks based on the end time and start time.

Run the unit test above and see the following results:

The 2021-09-11 23:33:38. 95891-842 the INFO/task - 3 com. Didispace. Chapter75. AsyncTasks: Start doing task three 23:33:38 2021-09-11. 842 INFO - 95891 [task - 2] com. Didispace. Chapter75. AsyncTasks: Start task two 23:33:38 2021-09-11. 95891-842 the INFO] [task - 1 com. Didispace. Chapter75. AsyncTasks: Began to do the 2021-09-11 23:33:45 task. The 95891-155 the INFO/task - 2 com. Didispace. Chapter75. AsyncTasks: To complete the task two, time-consuming: 6312 milliseconds 23:33:47 2021-09-11. 95891-308 the INFO/task - 3 com. Didispace. Chapter75. AsyncTasks: To complete the task 3, time-consuming: 8465 milliseconds 23:33:47 2021-09-11. 95891-403 the INFO] [task - 1 com. Didispace. Chapter75. AsyncTasks: To complete the task, a time-consuming: 8560 milliseconds 23:33:47 2021-09-11. 95891-404 the INFO [main] C.D.C. hapter75. Chapter75ApplicationTests: The total time for all tasks to complete: 8590 msCopy the code

As you can see, by making asynchronous calls, tasks one, two, and three execute concurrently, effectively reducing the total running time of the program. Spring Boot 2.x Basic Tutorial , welcome to collect and forward! If you encounter difficulties in learning? You can join our Spring technology exchange group, participate in exchange and discussion, better learning and progress!

Code sample

For the complete project of this article, see the chapter7-5 project in the 2.x directory of the warehouse below:

  • Github:github.com/dyc87112/Sp…
  • Gitee:gitee.com/didispace/S…

If you found this article good, welcomeStarSupport, your attention is my motivation!

Welcome to pay attention to my public account: program ape DD, share the outside can not see the dry goods and thinking!