Learning of asynchronous choreography completable future

Posted by dusty on Fri, 08 Oct 2021 19:56:42 +0200

When we have the following scenarios:

In the above, we can either query in the database or call the query remotely. The time spent is different, so we can easily think of using multithreading to solve this problem. However, if there are various troublesome sequences (for example, 5 should be executed after 1 and 2, and 6 should be executed after 2 and 3), it will be more complex for us to use multithreading at this time.
Now there is a class completabilefuture to encapsulate this complexity:

CompletableFuture

For each method of completable future class, there are two methods: providing thread pool and not providing thread pool. If thread pool is not provided, its custom thread pool will be used. We use the thread pool we provide here.

runAsync() and supplyAsync()

There are several basic methods for completable future: runAsync and supplyAsync. See the following examples to understand the difference:

ExecutorService executorService = Executors.newFixedThreadPool(10);

CompletableFuture<Void> async = CompletableFuture.runAsync(() -> {
            System.out.println("Currently executing");
        }, executorService);
CompletableFuture<String> async1 = CompletableFuture.supplyAsync(() -> {
            return "Return value";
        }, executorService);
        System.out.println(async1.get());

It can be found that runAsync() has no return value and supplyAsync() has a return value. The specific return value can be obtained by calling the get() method through the returned completabilefuture class

Processing after thread completion

The main methods are handle() and exceptionally(). The first method can get the return value and exception, and the second method can only get the exception.

   CompletableFuture.supplyAsync(()->{
            return "hello";
        },executorService).handle((t,u)->{
            return "The result is"+t+","+"Exceptions are:"+u ;
        })
        .exceptionally(e->{
            System.out.println();
            return "Return value after exception handling";
        });

Serialization

thenRunAsync()

This method can only be executed serially, and the second thread cannot receive any information from the first thread

CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
            return "Return value";
        }, executorService).thenRunAsync(() -> {
            System.out.println("");
        });

thenAcceptAsync()

This method can get the return value of the first thread:

CompletableFuture.supplyAsync(()->{
            return "hello";
        }).thenAcceptAsync(r->{
            System.out.println("The return value of the previous thread is"+r);
        },executorService);

thenApplyAsync

This method can not only get the return value of the previous thread, but also return the value itself:

CompletableFuture.supplyAsync(()->{
            return "hello";
        }).thenApplyAsync(r->{
            return "new"+r;
        },executorService);

Asynchronous processing with sequence:

Now let's solve the problem we first asked:

ExecutorService executorService = Executors.newFixedThreadPool(10);

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("Query commodity attribute information");
            return "iphone";
        }, executorService);

CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("Query product pictures");
            return "http://aaa.jpg";
        }, executorService);

CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
            System.out.println("Query product introduction");
            return "Iphone 13";
        }, executorService);

As mentioned above, three tasks are executed asynchronously, so we need the fifth task to be executed after 1 and 2, and the sixth task to be executed after 2 and 3, so we use the allof() method

  CompletableFuture<String> future5 = CompletableFuture.allOf(future1, future2).thenApplyAsync((r) -> {
            return "The fifth task is completed";
        }, executorService);
        System.out.println(future5.get());
        CompletableFuture<String> future6 = CompletableFuture.allOf(future2, future3).thenApplyAsync((r) -> {
            return "The sixth task is completed";
        }, executorService);
        System.out.println(future6.get());

When we execute the methods of future5.get() and future6.get(), future5 will block waiting for the completion of 1 and 2, and future6 will block waiting for the completion of 2 and 3,

In addition to the allof() method, there is also an anyof() method, which is to wait for one of the threads to execute and then execute the subsequent threads

Topics: Java Database