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.