preface

In the past, threads or Thread pool executors were used to create tasks that needed to be executed asynchronously. If a value is required, the Future is retrieved by calling executor.submit. But what can we do if multiple threads have dependency combinations? Synchronization components such as CountDownLatch and CyclicBarrier can be used; There’s an easy way to do this, which is to use CompletableFuture

  • Creation of threaded tasks
  • Serial execution of threaded tasks
  • Parallel execution of threaded tasks
  • Handle task results and exceptions
  • Simple combination of multiple tasks
  • Cancels execution of a thread task
  • Obtaining and judging task results

1. Create asynchronous thread tasks

Create the CompletableFuture task according to the supplier

// Use the built-in thread ForkJoinPool.commonPool() to perform tasks according to the supplier build
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
// Specify a custom thread to perform tasks according to the supplier build
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
Copy the code

Create the CompletableFuture task according to runnable

// Use the built-in thread ForkJoinPool.commonPool() to execute tasks according to the runnable build
public static CompletableFuture<Void> runAsync(Runnable runnable)
// Specify a custom thread to execute tasks based on runnable builds
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
Copy the code
  • Use the sample
ExecutorService executor = Executors.newSingleThreadExecutor();
CompletableFuture<Void> rFuture = CompletableFuture
        .runAsync(() -> System.out.println("hello siting"), executor);
/ / supplyAsync use
CompletableFuture<String> future = CompletableFuture
        .supplyAsync(() -> {
            System.out.print("hello ");
            return "siting";
        }, executor);

// Block wait, runAsync future returns no value, output null
System.out.println(rFuture.join());
// block wait
String name = future.join();
System.out.println(name);
executor.shutdown(); // Thread pool needs to be closed-------- Command output -------- hello sitingnull
hello siting
Copy the code

The constant value is returned as CompletableFuture

// Sometimes you need to build a constant CompletableFuture
public static <U> CompletableFuture<U> completedFuture(U value)
Copy the code

2. Threads execute serially

When the task is complete, the action is run, regardless of the result of a task, with no return value

public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor)
Copy the code
  • Use the sample
CompletableFuture<Void> future = CompletableFuture
        .supplyAsync(() -> "hello siting", executor)
        .thenRunAsync(() -> System.out.println("OK"), executor); executor.shutdown(); -------- The command output is -------- OKCopy the code

When the task is complete, the action is run, depending on the result of the previous task, with no return value

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
  • Use the sample
ExecutorService executor = Executors.newSingleThreadExecutor();
CompletableFuture<Void> future = CompletableFuture
        .supplyAsync(() -> "hello siting", executor) .thenAcceptAsync(System.out::println, executor); executor.shutdown(); -------- Command output -------- hello sitingCopy the code

When the task is complete, fn is run, depending on the result of the previous task, with 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
  • Use the sample
ExecutorService executor = Executors.newSingleThreadExecutor();
CompletableFuture<String> future = CompletableFuture
        .supplyAsync(() -> "hello world", executor)
        .thenApplyAsync(data -> {
            System.out.println(data); return "OK"; }, executor); System.out.println(future.join()); executor.shutdown(); -------- Command output -------- hello world OKCopy the code

ThenCompose – Run fn when the task is complete, depending on the result of the previous task, with a return value

  • Similar to thenApply (except thenCompose returns the CompletionStage and thenApply returns U), this method is provided to work well with other CompletableFuture tasks
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)        
Copy the code
  • Use the sample
// The first asynchronous task, the constant task
CompletableFuture<String> f = CompletableFuture.completedFuture("OK");
// The second asynchronous task
ExecutorService executor = Executors.newSingleThreadExecutor();
CompletableFuture<String> future = CompletableFuture
        .supplyAsync(() -> "hello world", executor)
        .thenComposeAsync(data -> {
            System.out.println(data); return f; // Use the first task as a return}, executor); System.out.println(future.join()); executor.shutdown(); -------- Command output -------- hello world OKCopy the code

3. Threads execute in parallel

The two CompletableFuture are executed in parallel, and then the action is executed, independent of the results of the last two tasks, with no return value

public CompletableFuture<Void> runAfterBoth(CompletionStage<? > other, Runnable action) public CompletableFuture<Void> runAfterBothAsync(CompletionStage<? > other, Runnable action) public CompletableFuture<Void> runAfterBothAsync(CompletionStage<? > other, Runnable action, Executor executor)Copy the code
  • Use the sample
// The first asynchronous task, the constant task
CompletableFuture<String> first = CompletableFuture.completedFuture("hello world");
ExecutorService executor = Executors.newSingleThreadExecutor();
CompletableFuture<Void> future = CompletableFuture
        // The second asynchronous task
        .supplyAsync(() -> "hello siting", executor)
        // () -> system.out.println ("OK") is the third task
        .runAfterBothAsync(first, () -> System.out.println("OK"), executor); executor.shutdown(); -------- The command output is -------- OKCopy the code

The two CompletableFuture are executed in parallel, and then the action is executed, depending on the result of the two tasks, with no return value

// If the first task is completed and other is run, fn will consume the results of the two tasks
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,
        BiConsumer<? super T, ? super U> action)
// Two tasks are completed asynchronously, and fn relies on consuming the results of the two tasks
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
        BiConsumer<? super T, ? super U> action)  
// Two tasks are completed asynchronously (the second task is executed with the specified thread pool), and the fn then relies on consuming the results of the two tasks, with no return value
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
        BiConsumer<? super T, ? super U> action, Executor executor) 
Copy the code
  • Use the sample
// The first asynchronous task, the constant task
CompletableFuture<String> first = CompletableFuture.completedFuture("hello world");
ExecutorService executor = Executors.newSingleThreadExecutor();
CompletableFuture<Void> future = CompletableFuture
        // The second asynchronous task
        .supplyAsync(() -> "hello siting", executor)
        // (w, s) -> system.out.println (s.thenAcceptBothAsync(first, (s, w) -> System.out.println(s), executor); executor.shutdown(); -------- Command output -------- hello sitingCopy the code

The two CompletableFuture are executed in parallel, and then the action is executed, depending on the result of the two tasks, with a return value

// If the first task is completed and other is run, fn will consume the results of the two tasks and return the value
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, 
  BiFunction<? super T,? super U,? extends V> fn)
// Two tasks are completed asynchronously, and fn then relies on consuming the results of the two tasks
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,
        BiFunction<? super T,? super U,? extends V> fn)   
// Two tasks are completed asynchronously (the second task is executed with the specified thread pool), and the fn then relies on consuming the results of the two tasks with a 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
  • Use the sample
// The first asynchronous task, the constant task
CompletableFuture<String> first = CompletableFuture.completedFuture("hello world");
ExecutorService executor = Executors.newSingleThreadExecutor();
CompletableFuture<String> future = CompletableFuture
        // The second asynchronous task
        .supplyAsync(() -> "hello siting", executor)
        // (w, s) -> system.out.println (s
        .thenCombineAsync(first, (s, w) -> {
            System.out.println(s);
            return "OK"; }, executor); System.out.println(future.join()); executor.shutdown(); -------- Command output -------- hello siting OKCopy the code

4, thread parallel execution, who finished the first who trigger the next task (two choose the fastest)

When the previous task or other task is complete, the action is executed, and no value is returned regardless of the result of the previous task

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
  • Use the sample
// The first asynchronous task is hibernated for 1 second to ensure the latest execution
CompletableFuture<String> first = CompletableFuture.supplyAsync(()->{
    try{ Thread.sleep(1000); }catch (Exception e){}
    System.out.println("hello world");
    return "hello world";
});
ExecutorService executor = Executors.newSingleThreadExecutor();
CompletableFuture<Void> future = CompletableFuture
        // The second asynchronous task
        .supplyAsync(() ->{
            System.out.println("hello siting");
            return "hello siting";
        } , executor)
        //() -> system.out.println ("OK") is the third task
        .runAfterEitherAsync(first, () ->  System.out.println("OK") , executor); executor.shutdown(); -------- Command output -------- hello siting OKCopy the code

When the previous task or other task is completed, the action is executed, depending on the result of completing the task first, and no value is returned

public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other,
  Consumer<? super T> action)
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other,
  Consumer<? super T> action, Executor executor)       
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other,
  Consumer<? super T> action, Executor executor)     
Copy the code
  • Use the sample
// The first asynchronous task is hibernated for 1 second to ensure the latest execution
CompletableFuture<String> first = CompletableFuture.supplyAsync(()->{
    try{ Thread.sleep(1000);  }catch (Exception e){}
    return "hello world";
});
ExecutorService executor = Executors.newSingleThreadExecutor();
CompletableFuture<Void> future = CompletableFuture
        // The second asynchronous task
        .supplyAsync(() -> "hello siting", executor)
        // data -> system.out.println (data) is the third task.acceptEitherAsync(first, data -> System.out.println(data) , executor); executor.shutdown(); -------- Command output -------- hello sitingCopy the code

The last task or other task completed, runs fn, depending on the result of the first task completed, has a 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
  • Use the sample
// The first asynchronous task is hibernated for 1 second to ensure the latest execution
CompletableFuture<String> first = CompletableFuture.supplyAsync(()->{
    try{ Thread.sleep(1000);  }catch (Exception e){}
    return "hello world";
});
ExecutorService executor = Executors.newSingleThreadExecutor();
CompletableFuture<String> future = CompletableFuture
        // The second asynchronous task
        .supplyAsync(() -> "hello siting", executor)
        // data -> system.out.println (data) is the third task
        .applyToEitherAsync(first, data ->  {
            System.out.println(data);
            return "OK"; } , executor); System.out.println(future); executor.shutdown(); -------- Command output -------- hello siting OKCopy the code

5. Handle task results or exceptions

Exceptionally – handles exceptions

public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
Copy the code
  • If the previous handler has an exception, the exceptionally call is triggered. catch
  • Use the sample
CompletableFuture<Integer> first = CompletableFuture
        .supplyAsync(() -> {
            if (true) {
                throw new RuntimeException("main error!");
            }
            return "hello world";
        })
        .thenApply(data -> 1)
        .exceptionally(e -> {
            e.printStackTrace(); // Exception capture processing, the first two processing links can be captured daily
            return 0;
        });
Copy the code

Handle – Run the FN when the task is complete or abnormal, and return the return value of the FN

  • Exceptionally, it handles both the exception from the previous cycle and its normal return value
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
  • Use the sample
CompletableFuture<Integer> first = CompletableFuture
        .supplyAsync(() -> {
            if (true) { throw new RuntimeException("main error!"); }
            return "hello world";
        })
        .thenApply(data -> 1)
        .handleAsync((data,e) -> {
            e.printStackTrace(); // Exception capture processing
            returndata; }); System.out.println(first.join()); Output -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- java.util.concurrent.Com pletionException: Java. Lang. RuntimeException: the main error! .5 more
null
Copy the code

WhenComplete – Run the action when the task is complete or an exception, with a return value

  • WhenComplete differs from Handle in that it does not participate in the processing of the return result; it can be used as a listener
  • Even if the exception is handled, the exception reappears on the outer layer of the CompletableFuture
  • When using whenCompleteAsync to return a result, you need to consider multithreading, since two threads can work on a result at the same time
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)        
Copy the code
  • Use the sample
CompletableFuture<AtomicBoolean> first = CompletableFuture
        .supplyAsync(() -> {
            if (true) {  throw new RuntimeException("main error!"); }
            return "hello world";
        })
        .thenApply(data -> new AtomicBoolean(false))
        .whenCompleteAsync((data,e) -> {
            // Exception capture is handled, but the exception is still regenerated in the outer layerSystem.out.println(e.getMessage()); }); first.join(); Output -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - Java. Lang. RuntimeException: the main error! Exceptionin thread "main"java.util.concurrent.CompletionException: java.lang.RuntimeException: main error! .5 more
Copy the code

6. Simple combinations of tasks

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

  • Use the sample
 CompletableFuture<Void> future = CompletableFuture
        .allOf(CompletableFuture.completedFuture("A"),
                CompletableFuture.completedFuture("B"));
// All tasks need to be completed
future.join();
CompletableFuture<Object> future2 = CompletableFuture
        .anyOf(CompletableFuture.completedFuture("C"),
                CompletableFuture.completedFuture("D"));
// One of the tasks is complete
future2.join();
Copy the code

7. Cancel the execution of the thread task

// mayInterruptIfRunning has no impact; If the task is not complete, an exception is returned
public boolean cancel(boolean mayInterruptIfRunning) 
// Whether to cancel the task
public boolean isCancelled()
Copy the code
  • Use the sample
CompletableFuture<Integer> future = CompletableFuture
        .supplyAsync(() -> {
            try { Thread.sleep(1000);  } catch (Exception e) { }
            return "hello world";
        })
        .thenApply(data -> 1);

System.out.println("Before mission cancellation :" + future.isCancelled());
// If the task is not completed, an exception is returned. The result is exceptionally and handle is required
future.cancel(true);
System.out.println("After mission canceled :" + future.isCancelled());
future = future.exceptionally(e -> {
    e.printStackTrace();
    return 0; }); System.out.println(future.join()); -------- Command output -------- Before the task is canceled:falseAfter the task is cancelled:true
java.util.concurrent.CancellationException
 at java.util.concurrent.CompletableFuture.cancel(CompletableFuture.java:2276)
 at Test.main(Test.java:25)
0
Copy the code

8. Task acquisition and completion judgment

// Check whether the task is completed
public boolean isDone()
// Block waiting to get the return value
public T join()
// Block waiting for the return value, the difference is that get returns the checked exception
public T get()
// Wait to block for a while and get the return value
public T get(long timeout, TimeUnit unit)
// Return value if not completed
public T getNow(T valueIfAbsent)
// If the task is not completed, use value as the result of executing the task, and the task is finished. Future.get is required
public boolean complete(T value)
// If the task is not completed, it is an exception call
public boolean completeExceptionally(Throwable ex)
// Check whether the task ended due to an exception
public boolean isCompletedExceptionally()
// Force the return value to value, whether or not the task was completed; Similar to complete
public void obtrudeValue(T value)
// Force the exception to be thrown and returned, regardless of whether the task was completed; Similar completeExceptionally
public void obtrudeException(Throwable ex) 
Copy the code
  • Use the sample
CompletableFuture<Integer> future = CompletableFuture
        .supplyAsync(() -> {
            try { Thread.sleep(1000);  } catch (Exception e) { }
            return "hello world";
        })
        .thenApply(data -> 1);

System.out.println("Before the mission is completed :" + future.isDone());
future.complete(10);
System.out.println("After the mission is completed :"+ future.join()); -------- Command output -------- Before the task is complete:falseAfter the task is completed:10
Copy the code

conclusion

Java multithreading has been the focus of the interview, but also an important reflection of ability to improve, how to do calm, calm face, we need to integrate the content of which, small make up here also corresponding to a multithreaded – concurrent programming mind map, need friends can see, concern public number: Kylin fixed bug, You can also get a copy that includes Java basics, Java Collections containers, Java exceptions, concurrent programming, JVM, Spring, Spring MVC, Spring Boot, Spring Cloud, MyBatis, Redis, MySQL database, message middleware MQ and RabbitMQ, Dubbo, Linux, Tomcat, ZooKeeper, Netty, architecture design & distributed & data structure and algorithm, etc., are all the interview questions of Internet big factory. Some fans have already won offers from many big factories with this PDF.

Welcome everyone to exchange, like the article remember to pay attention to me like yo, thank you for your support!