This is the first day of my participation in the August More Text challenge

Asynchronous computation

An asynchronous call is simply a way to implement an operation without waiting for a return value from the called function. Let’s say you take a bag of clothes to your favorite dry cleaner. The dry cleaner will give you an invoice telling you when your clothes will be ready. In the meantime, you can do something else. Laundry is an asynchronous process for you.

How do you use asynchrony in your development?

The Future interface

Future is a new interface in JDK5 that describes the result of an asynchronous calculation. It provides a way to check that a calculation is complete, wait for it to complete, and get the results of the calculation. After the calculation is complete, only the get method can be used to obtain the results. Perform a time-consuming operation in an asynchronous manner using the Future to complete the laundry example above.

public class FutureDemo { public static void main(String[] args) throws ExecutionException, InterruptedException { //1. Create the ExecutorService through which you can submit a task to a thread pool ExecutorService executor = Executors. NewCachedThreadPool (); ExecutorService Future<String> Future = executor.submit(()->{try {system.out.println (" take your clothes to the dry cleaner "); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return "finish "; }); //3. GoShopping (); System.out.println(future.get())); System.out.println("finish!" ); }}Copy the code

CompletableFuture

Why is there a Future? After JDK8, CompletableFuture is provided, because Future has its limitations; For example:

  • Merge two asynchronous computations into one;
  • Wait for all tasks to complete for multiple futures
  • Just wait for the fastest finished task in the Future collection to complete and return its result
  • Once the Future is complete, there are a few things you need to do.

CompletableFuture implements both the Future interface and the CompletionStage interface, which is also a new interface in JDK8. It provides the function programming capability for CompletableFuture. We can stream calls multiple times on a CompletableFuture result to get the final result.

Creating asynchronous objects

CompletableFuture provides the runAsync, supplyAsync static method to create an asynchronous operation. RunAsync scenarios where there is no return value, such as performing a simple asynchronous operation. SupplyAsync SCENARIOS where results can be returned, such as calculating some data to return. In the meantime, both methods can be set by passing in the executor thread pool, or by using the default ForkJoinPool.mon thread pool.

// No return value runAsync() public static CompletableFuture<Void> runAsync(Runnable Runnable) public static CompletableFuture<Void> runAsync(Runnable runnable, Executor Executor) // Return value supplyAsync() public static <U> CompletableFuture<U> supplyAsync(Supplier<U> Supplier) public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)Copy the code
public class CompletableFutureDemo { public static void main(String[] args) throws ExecutionException, InterruptedException {// Asynchronous call, Without a return value CompletableFuture < Void > CompletableFuture = CompletableFuture. RunAsync (() - > { System.out.println(Thread.currentThread().getName()); }); completableFuture.get(); / / asynchronous calls, returns a value of CompletableFuture < Integer > future. = CompletableFuture supplyAsync (() - > {System. Out. Println (" build tasks "); return calc(15, 10); }); System.out.println(future.get()); } public static Integer calc(Integer a, Integer b) {try {thread.sleep (1000); } catch (InterruptedException e) { e.printStackTrace(); } return a / b; }}Copy the code
ForkJoinPool.com monpool-worker-1 Build task 1Copy the code

Call back when done

whenComplete

CompletableFuture provides an asynchronous orchestration feature, using whenComplete, which calls a callback when the Future task is complete.

Public CompletableFuture<T> whenComplete(whenonsumer <? super T, ? Super Throwable> action) //whenCompleteAsync, Public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? Super Throwable> action) //whenCompleteAsync, Public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable>action, Executor executor)Copy the code

WhenComplete handles normal and exception calculations; exception cases are exceptionally handled. WhenComplete is when the thread executing the current task continues to execute whenComplete’s task. WhenCompleteAsync is submitted to other thread pools for execution.

WhenComplete takes a BiConsumer as an argument, and BiConsumer receives two arguments, one for the result of the last task and one for the exception of the last task. So you can get the execution result of the task and the exception, and CompletableFuture can handle the exception, given a default return when an exception occurs.

    CompletableFuture<Integer> future = CompletableFuture.supplyAsync(()->{
            return 10/0;
        }).whenComplete((res, exception)->{
            System.out.println(res);
            System.out.println(exception);
        }).exceptionally(throwable -> 10);

        System.out.println(future.get());
Copy the code
null
java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
10
Copy the code

handle

The Handle method is much the same as whenComplete.

    public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)

    public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn)

    public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor)
Copy the code

The difference is that whenComplete passes in BiConsumer, while Handle passes in BiFunction. Got it! The handle method returns a value that can be used to process the execution result of the CompletableFuture task and return a new result, while whenComplete cannot.

CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(()->{ return 10/2; }).handle((res, exception)->{ if (res! =null) {// Handle returns res * res; } return 0; }); System.out.println(future2.get()); //get() returns result 25Copy the code

Task serial

Serial is the continuation of the next task after the completion of one task. CompletableFuture provides the following methods.

thenRun/thenRunAsync

After a task is executed, we can see that the parameter passed is a Runnbale, and the subsequent tasks do not depend on the results of the previous task execution.

Public CompletableFuture<Void> thenRun(Runnable action) public CompletableFuture<Void> thenRunAsync(Runnable action) public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor)Copy the code

For example,

CompletableFuture.runAsync(()->{ System.out.println("task 1 finish!" ); }).thenRun(()->{ System.out.println("task 2 finish!" ); }).get();Copy the code

thenAccept/thenAcceptAsync

ThenAccept differs from runAsync in that its argument is a Consumer, which can return the result of the previous task.

public CompletableFuture<Void> thenAccept(Consumer<? super T> action)

public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)

public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor)
Copy the code
CompletableFuture.supplyAsync(()->{ System.out.println("task 1 finish!" ); return "Hello"; }).thenAccept((res)->{ System.out.println("this is task 2, task 1 return :"+res); }).get(); ----------Console print--------------- task 1 finish! this is task 2, task 1 return :HelloCopy the code

thenApply/thenApplyAsync

Deja vu. ThenApply differs from thenAccept in that it has a return value.

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)
Copy the code
String s = CompletableFuture.supplyAsync(() -> { System.out.println("task 1 finish!" ); return "Hello"; }).thenApply((res) -> { System.out.println("this is task 2, task 1 return :" + res); return res + " world"; }).get(); System.out.println(s); ----------Console print--------------- task 1 finish! this is task 2, task 1 return :Hello Hello worldCopy the code

Two-task combination

runAfterBoth/runAfterBothAsync

Combine two CompletableFutures and execute them without relying on their results. When both completableFutures have been executed, execute the action.

// All the same, just stick the custom thread pool runAfterBothAsy here. public CompletableFuture<Void> runAfterBothAsync(CompletionStage<? > other, Runnable action, Executor executor)Copy the code

For example,

CompletableFuture.runAsync(()->{ System.out.println("CompletableFuture 2."); }).runAfterBoth(CompletableFuture.runAsync(()->{ System.out.println("CompletableFuture 1."); }), ()->{ System.out.println("all finish!" ); }).join();Copy the code

thenAcceptBoth/thenAcceptBothAsync

Combine two CompletableFutures, get their return results, and execute the action with no return value.

public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor)
Copy the code
CompletableFuture.supplyAsync(()->{ System.out.println("CompletableFuture 1."); return 1; }).thenAcceptBoth(CompletableFuture.supplyAsync(()->{ System.out.println("CompletableFuture 2."); return 2; }), (f1, f2)->{ System.out.println("Result of CompletableFuture 1 is "+f1); System.out.println("Result of CompletableFuture 2 is "+f2); System.out.println("all finish!" ); }).join(); ----------------Console Print----------------- CompletableFuture 1. CompletableFuture 2. Result of CompletableFuture 1 is 1 Result of CompletableFuture 2 is 2 all finish!Copy the code

thenCombine/thenCombineAsync

Combine two CompletableFutures, get their return result, and then execute the action with the return value.

public <U,V> CompletableFuture<V> thenCombineAsync(
        CompletionStage<? extends U> other,
        BiFunction<? super T,? super U,? extends V> fn, Executor executor)
Copy the code

Seeing this, I believe there is no need to write demo…

runAfterEither/runAfterEitherAsync

Combine two CompleTableFutures and execute the action when one CompletableFuture is complete.

public CompletableFuture<Void> runAfterEither(CompletionStage<? > other,Runnable action) public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<? > other,Runnable action) public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<? > other,Runnable action,Executor executor)Copy the code

acceptEither/acceptEitherAsync

Combine two CompleTableFutures, and after one CompletableFuture has been executed, accept their return and execute the action with no return value.

    public CompletableFuture<Void> acceptEither(
        CompletionStage<? extends T> other, Consumer<? super T> action)

    public CompletableFuture<Void> acceptEitherAsync(
        CompletionStage<? extends T> other, Consumer<? super T> action)

    public CompletableFuture<Void> acceptEitherAsync(
        CompletionStage<? extends T> other, Consumer<? super T> action,
        Executor executor)
Copy the code

applyToEither/applyToEitherAsync

Combine two CompleTableFutures, and after one CompletableFuture has been executed, accept their return and execute the action with no return value.

    public <U> CompletableFuture<U> applyToEither(
        CompletionStage<? extends T> other, Function<? super T, U> fn)

    public <U> CompletableFuture<U> applyToEitherAsync(
        CompletionStage<? extends T> other, Function<? super T, U> fn)

    public <U> CompletableFuture<U> applyToEitherAsync(
        CompletionStage<? extends T> other, Function<? super T, U> fn,
        Executor executor)
Copy the code

multitasking

There are two tasks above, what if you need more than one?

AllOf is to perform subsequent operations after multiple tasks are completed; AnyOf means that any task can be completed and subsequent operations can be performed.

public static CompletableFuture<Void> allOf(CompletableFuture<? >... cfs) public static CompletableFuture<Object> anyOf(CompletableFuture<? >... cfs)Copy the code

For example,

  CompletableFuture exportSheet1 = CompletableFuture.runAsync(()->{
            System.out.println("export sheet1");
        });
        CompletableFuture exportSheet2 = CompletableFuture.runAsync(()->{
            System.out.println("export sheet2");
        });
        CompletableFuture exportSheet3 = CompletableFuture.runAsync(()->{
            System.out.println("export sheet3");
        });

        CompletableFuture.allOf(exportSheet1, exportSheet2, exportSheet3).join();
Copy the code

conclusion

CompletableFuture is pretty powerful and provides a lot of methods, so it looks like it’s nothing more than whether the asynchronous task returns a result, whether the task needs to get the result of the last task, and whether it needs to return a result or not. Summary here, I hope to help you.