Byte Architect: do you understand several ways of Java asynchronous call?

In daily development, it is often encountered that the foreground calls the service, then triggers a time-consuming asynchronous service, and returns the original service without waiting for the processing result of the asynchronous task. Here is a knowledge of Java asynchronous call. The following article attempts to summarize the various ways of Java asynchronous invocation.

1, By creating a new thread

First of all, we must realize that the essence of * * asynchronous call is actually executed by starting a new thread** For example:

public static void main(String[] args) throws Exception{

    System.out.println("Main thread =====> start =====> " + System.currentTimeMillis());

    new Thread(() -> {
        System.out.println("Asynchronous thread =====> start =====> " + System.currentTimeMillis());
        try{
            Thread.sleep(5000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("Asynchronous thread =====> end =====> " + System.currentTimeMillis());
    }).start();

    Thread.sleep(2000);

    System.out.println("Main thread =====> end =====> " + System.currentTimeMillis());

}

The data results are as follows. We know that system Currenttimemillis() time unit is ms.

Main thread =====> start =====> 1627893837146
 Asynchronous thread =====> start =====> 1627893837200
 Main thread =====> end =====> 1627893839205
 Asynchronous thread =====> end =====> 1627893842212

We use thread hibernation to achieve the effect that the execution time of the main thread is about 2 seconds and that of the asynchronous thread is about 5 seconds. Through the penultimate bit (second bit) of the printed timestamp, we can see that the total execution time of the two threads is about 5 seconds, which is in line with the characteristics of asynchronous execution

The above is the lambda writing method of using Runable to realize multi-threaded creation. For lambda knowledge, please refer to Java Lambda expression ; As for multiple implementations of multithreading, Java multithreaded transaction management It is mentioned in the article, which can be viewed step by step

2, Through thread pool

Because the implementation of asynchronous tasks is essentially performed by new threads, asynchronous execution can also be realized through the implementation of thread pool. It is written in the same way that we use thread pool to start multithreading. However, since our purpose is not to execute multithreading, but to execute tasks asynchronously, we generally need another thread.

Therefore, different from the commonly used newFixedThreadPool for executing multithreaded tasks, we use newsinglethreadexecution to create a thread pool for a single thread when executing asynchronous tasks.

public static void main(String[] args) throws Exception{

    System.out.println("Main thread =====> start =====> " + System.currentTimeMillis());

    ExecutorService executorService = Executors.newSingleThreadExecutor();
    executorService.submit(()->{
        System.out.println("Asynchronous thread =====> start =====> " + System.currentTimeMillis());
        try{
            Thread.sleep(5000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("Asynchronous thread =====> end =====> " + System.currentTimeMillis());
    });
    executorService.shutdown(); // Recycle thread pool

    Thread.sleep(2000);

    System.out.println("Main thread =====> end =====> " + System.currentTimeMillis());

}

The results are as follows:

Main thread =====> start =====> 1627895467578
 Asynchronous thread =====> start =====> 1627895467635
 Main thread =====> end =====> 1627895469644
 Asynchronous thread =====> end =====> 1627895472649

It can be seen that the result is basically consistent with the first result.

Warm tip: don't forget to recycle the thread pool

3, Annotation via @ Async

As we all know, a very important feature of SpringBoot project is annotation. If your project is SpringBoot, you have another choice - @ Async annotation.

It is also very simple to use. Encapsulate the code to be executed asynchronously into a method, annotate the method with @ Async, and then call it directly in the main method.

@Test
public void mainThread() throws Exception{

    System.out.println("Main thread =====> start =====> " + System.currentTimeMillis());
    collectionBill.asyncThread();
    Thread.sleep(2000);
    System.out.println("Main thread =====> end =====> " + System.currentTimeMillis());

    Thread.sleep(4000); // Used to prevent the jvm from stopping, resulting in asynchronous thread interruption
}

@Async
public void asyncThread(){
    System.out.println("Asynchronous thread =====> start =====> " + System.currentTimeMillis());
    try{
        Thread.sleep(5000);
    }catch (InterruptedException e){
        e.printStackTrace();
    }
    System.out.println("Asynchronous thread =====> end =====> " + System.currentTimeMillis());
}

The results are as follows:

Main thread =====> start =====> 1627897539948
 Asynchronous thread =====> start =====> 1627897539956
 Main thread =====> end =====> 1627897541965
 Asynchronous thread =====> end =====> 1627897544966

There are two points to note:

  1. Similar to the @ transitional annotation, the method of @ Async annotation and the calling method cannot be in the same class, otherwise it will not take effect
  2. The design of JUnit framework does not consider the multithreading scenario, so after the main thread exits, the child thread will also exit immediately, so you can add multithread sleep time to observe the execution of asynchronous threads

4, Through completable future

Completable Future is jdk1 8 is an extension of Future. Completabilefuture implements the CompletionStage interface and Future interface, and increases the capabilities of asynchronous callback, streaming processing and multiple Future combination processing.

The implementation code is as follows:

public static void main(String[] args) throws Exception{

    System.out.println("Main thread =====> start =====> " + System.currentTimeMillis());

    ExecutorService executorService = Executors.newSingleThreadExecutor();
    CompletableFuture.runAsync(() ->{
        System.out.println("Asynchronous thread =====> start =====> " + System.currentTimeMillis());
        try{
            Thread.sleep(5000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("Asynchronous thread =====> end =====> " + System.currentTimeMillis());
    },executorService);
    executorService.shutdown(); // Recycle thread pool

    Thread.sleep(2000);

    System.out.println("Main thread =====> end =====> " + System.currentTimeMillis());

}

Similar results can also be achieved as follows:

Main thread =====> start =====> 1627898354914
 Asynchronous thread =====> start =====> 1627898354977
 Main thread =====> end =====> 1627898356980
 Asynchronous thread =====> end =====> 1627898359979

Completable future has very powerful functions and can bring us a very smooth programming experience. An article will be written later to introduce completable future in detail

Classification: [Java basics]

Tag: [multithreading]

Keywords: Java Back-end Multithreading IDE

Added by laserlight on Tue, 28 Dec 2021 15:08:31 +0200