Asynchronous Blade CompletableFuture

What is CompletableFuture?

The CompletableFuture class implements the Future and CompletionStage interfaces and adds many new methods. It supports lambda and uses non-blocking methods through callbacks to improve the asynchronous programming model. Simply put, it can help us achieve task scheduling. [All the codes in this article have been uploaded to Code Cloud]

Creation of CompletableFuture

Let’s first look at an example of creation, and then expand to talk about the methods in this class:

CompletableFuture<String> completableFuture = new CompletableFuture<>();
completableFuture.complete("Hello CompletableFuture");
System.out.println(completableFuture.get());

It should be noted that when we call the get method on the incomplete CompleteableFuture, the get call will always be blocked because the Future is not completed.

Create an asynchronous task

CompletableFuture provides four static methods to create asynchronous tasks.

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor);

public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);

A method that does not specify an Executor will use ForkJoinPool.commonPool() as its thread pool to execute asynchronous code; if a thread pool is specified, it will run using the specified thread pool.

 static Executor screenExecutor(Executor e) {
        if (!useCommonPool & amp; & amp; e == ForkJoinPool. commonPool())
            return asyncPool;
        if (e == null) throw new NullPointerException();
        return e;
    }

The difference between the two is obvious, supplyAsync has a return value, and the runAsync method has no return value. Through the static method, we will immediately start the asynchronous thread to execute the tasks submitted by Supplier or Runnable. After the task execution is completed, the return value can be printed, and no other thread is required to actively call complete to indicate the completion of the task execution.

Get task execution result

public T get();
public T get(long timeout, TimeUnit unit);
public T getNow(T valueIfAbsent);
public T join();

get() and get(long timeout, TimeUnit unit) are functions that implement the Future interface. The main difference between the two is that get() will block until the result is obtained, and the value of get(long timeout, TimeUnit unit) can specify the timeout time. When the task has not been obtained by the specified time, a TimeoutException will be thrown.

getNow(T valueIfAbsent): It is to obtain the execution result of the task without blocking. If the task has not been executed, it will return the valueIfAbsent parameter value you passed in. If the execution is completed, it will return the result of the task execution.

join(): The main difference from get() is that get() will throw an exception when checking, but join() will not.

Processing after task completion

whenComplete

public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)

whenComplete is to continue to execute the action of whenComplete after the task is completed. It can also be seen here that the main thread executes the action.

If there is an exception, an exception will be thrown when the main thread gets the task result.

And whenCompleteAsync executes action through asynchronous thread

exceptionally

When an exception occurs during task execution, the callback specified by the exceptionally method will be called back, but if no exception occurs, it will not be called back.

thenApply

When thread B depends on the execution result of thread A, the thenApply method can be used to serialize the two threads

public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)

Use the result (Integer) in the supplyAsync method as the input parameter of the apply method of the Function interface in the thenApply method

If there is an exception thenApply will not execute

handle

public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn,Executor executor);

Different from thenApply, the method in the handle will be executed after supplyAsync/runAsync is executed.

thenAccept

public CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor);

Different from the above methods, thenAccept has no return value for pure consumption.

Combination tasks

thenCompose

This method is also the result that thread B needs to use thread A. What is different from thenApply is: thenCompose() is used to connect two CompletableFutures, and the return value is a new CompletableFuture; thenApply() converts the type in the generic type, which is the same A CompletableFuture.

public <U> CompletableFuture<U> thenCompose(Function<? super T,? extends CompletionStage<U>> fn)
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T,? extends CompletionStage<U>> fn)
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T,? extends CompletionStage<U>> fn, Executor executor)

thenCombine

After the completion of the two CompletableFuture tasks, the results will be handed over to thenCombine for processing, and a new CompletableFuture task will be generated.

public <U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn,Executor executor);

Who calls thenCombine, the first parameter of BiFunction is its result.

Other methods

allOf

The input parameters of the allOf method are multiple CompletableFuture tasks, and the return type is CompletableFuture. The allOf method waits for all CompletableFutures to be executed before performing calculations. Generally, it will be followed by a chained thenApply method or thenAccept method for all asynchronous tasks. Tasks are aggregated.

anyOf

The input parameter of the anyOf method is also multiple CompletableFuture tasks, and the return type is CompletableFuture. The anyOf method will be executed as long as there is one CompletableFuture task.

The article has been included in the official knowledge file Java skill tree Java asynchronous taskFuture and CompletableFuture119860 people are studying systematically

syntaxbug.com © 2021 All Rights Reserved.