An asynchronous call is simply a method that allows an operation to continue without waiting for a return value from the called function. In the Java language, it is simply a matter of starting another thread to complete part of the calculation in the call, so that the call can continue to run or return without waiting for the calculation result. But the caller still needs to fetch the result of the thread’s calculation.


In the era of concurrency, we often use multithreading for asynchronous programming, but one disadvantage of multithreading is that it is not easy to get the value of the thread after execution, although it can be obtained with some strange tricks, but this has been far from our original intention. Java 1.5 provides a Future interface that cancels execution status in a thread, determines isCancelled, isDone. Using multithreading to obtain the value after asynchronous execution, but it also has many disadvantages, such as

  1. A future does not have an exception handler, and usually the thread blocks inside the future after an exception is raised, so the exception cannot be thrown and the thread cannot exit

The get timeout method in the Future can solve this problem, but if the method has not finished executing and the timeout has expired, it will cause a timeout exception

V get(long timeout, TimeUnit unit)
Copy the code

  1. I can’t do a chain call, like IF I do this asynchronous task, and THEN I want to use the result of that call to do the next task, the future can’t do that directly.
  2. There is no way to evaluate the results of multiple calls in parallel, such as if I exit after only one of them succeeds and no more methods are executed, or if I want to evaluate all of them in parallel.
  3. No other calculation can be done without blocking, but only after the blocking method GET

There are a lot of things to consider if you want to use future well. In Java 8, the CompletableFuture was introduced, which implements the Future interface and the CompletionStage interface, specifies the specifications for asynchronous computing contracts, and provides their implementation.

Roughly categorize:

  1. Methods that end with async will execute the task from the incoming thread pool or the default ForkJoin thread pool, otherwise the task will continue with the thread that just executed the task
  2. The method that starts with then continues the CompletableFuture execution, implementing the chain call
  3. The function with accept in the middle is passed in as a Consumer interface that takes the value of the previous function and consumes it without returning any results
  4. A function with apply in the middle passes a functional funtion interface that takes the value of the last result and returns the calculated value, which can be received with get()
  5. A function with run in the middle, passing in a Runnable type, receives no value and consumes no value.
  6. Combine or both in between is triggered by two stages, and the effect of their two results can be calculated. The difference is that Combine has a return value, while BOTH has no return value
  7. The Compose method with Compose in the middle means the same thing as the apply method, but the difference is that the Compose method will generate a new CompletableFuture object and continue to execute, and apply will continue to connect to the CompletableFuture object that was just passed in to execute the function

The above function apis are compatible with each other, such as thenApplyAsync executing the last function, functional interfaces, and new threads executing asynchronously

Note that forkjoin threads execute as daemons. In the virtual machine, the main thread exits after the non-daemons finish executing. The daemons do not see results on the console


Let’s start by simply generating a CompletableFuture

CompletableFuture<String> completableFuture = new CompletableFuture<String>();
Copy the code

Then get the result using the GET method

String result = completableFuture.get();
Copy the code

And of course our completableFuture doesn’t do anything, so the get method is always blocking, and we need to execute the complete method before the GET, okay

completableFuture.complete("Future's shuaizx");
Copy the code

Or we can throw an exception before the get method

completableFuture.completeExceptionally(new RuntimeException());
Copy the code

Everything is as we expected, but remember that we need asynchronous execution, so we start a thread to execute these methods

        CompletableFuture<String> completableFuture=new CompletableFuture<>();
        new Thread(new Runnable() {
         @Override
         public void run() {
             completableFuture.complete("shuaizx");
         }
     }).start();


        System.out.println("start");
        System.out.println(completableFuture.get());
        System.out.println("end");
Copy the code

There is no difference between doing this and a future. If you throw an exception in a new thread, the external GET will not get the result and the execution will not be interrupted.

    public static String get() {
        if(true){
            throw new  RuntimeException();
        }
        return "shuaizx";
    }

    public static void main(String[] args) throws Exception {
        CompletableFuture<String> completableFuture = new CompletableFuture<>();
        new Thread(new Runnable() {
            @Override
            public void run() {
                String value = get();
                completableFuture.complete(value);
            }
        }).start();


        System.out.println("start");
        System.out.println(completableFuture.get());
        System.out.println("end");

Copy the code

We need to catch exceptions on the thread, reassemble a completeExceptionally and throw them out, which at least allows us to handle them externally and stop getting from blocking

    public static String get() {
        if(true){
            throw new  RuntimeException();
        }
        return "shuaizx";
    }

    public static void main(String[] args) throws Exception {
        CompletableFuture<String> completableFuture = new CompletableFuture<>();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    String value = get();
                    completableFuture.complete(value);
                }catch (Exception e){
                    completableFuture.completeExceptionally(e);
                }
            }
        }).start();


        System.out.println("start");
        System.out.println(completableFuture.get());
        System.out.println("end");
Copy the code

At least we don’t have to worry about the exception not being thrown, but CompletableFuture already gives us static methods that don’t have to write spaghetti code every time. Here are some of them:

CompletableFuture.runAsync(Runnable runnable);
CompletableFuture.runAsync(Runnable runnable, Executor executor);

CompletableFuture.supplyAsync(Supplier<U> supplier);
CompletableFuture.supplyAsync(Supplier<U> supplier, Executor executor)
Copy the code

The runAsync function takes a Runnable parameter and returns no value. Supplier provides Java 8 with a functional interface, which is essentially a container for storing executed values and then calling the get method at the end. If no Executor thread pool parameter is passed, the system assigns a custom ForkJoinPool thread to generate the thread. Perform tasks.

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}
Copy the code

Take a simple example

        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(()->"shuaizx");
        
        System.out.println("start");
        System.out.println(completableFuture.get());
        System.out.println("end");
Copy the code

CompletableFuture. RunAsync (Runnable Runnable) method accepts Runnable parameter, for example:

        CompletableFuture.runAsync(() -> {
            System.out.println("start");
        });

Copy the code

This is as simple as implementing a custom asynchronous execution plan

The basic API explains CompletableFuture


  1. Consumer interface

ThenAccept passes to a Consumer interface that consumes nothing and does not return a value. ThenAcceptAsync passes to a custom thread pool or forkJoin thread pool. The following Consumer apis follow this rule, but I won’t go into details

ThenAcceptBoth is used to merge the results and execute the provided action when both CompletionStages execute normally


        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            return 10;
        });
        System.out.println(future.thenAcceptBoth(CompletableFuture.supplyAsync(() -> {
            return 20;
        }),(x,y) -> System.out.println(x+y)).get());
Copy the code

WhenComplete consumption-type interface, passing in data and outliers that continue regardless of whether an exception is present, consuming and not returning

        CompletableFuture<String> futureSatrt = CompletableFuture.supplyAsync(() -> {
            return "start";
        }).whenComplete((t,e)->{
            System.out.println(t);
        });

        System.out.println(futureSatrt.get());
Copy the code

The allOf method passes in multiple completablefutures and waits until allOf them are executed before returning the result, but returns a void that needs to be retrieved manually, so it’s not a good API for getting allOf the result types


        CompletableFuture<String> futureSatrt = CompletableFuture.supplyAsync(() -> {
            System.out.println("start is run");
            return "start";
        });
        CompletableFuture<String> futureEnd = CompletableFuture.supplyAsync(() -> {
            System.out.println("end is run");
            return "end";
        });

        System.out.println( CompletableFuture.allOf(futureSatrt, futureEnd).get());
Copy the code

Fortunately, the java8 Stream makes it easy to get the results of the CompletableFuture run

But get must throw a checked exception. Can we get the result without throwing a checked exception? CompletableFuture also provides an unchecked exception that lets us simply get the result: join

        CompletableFuture<String> futureSatrt = CompletableFuture.supplyAsync(() -> {
            System.out.println("start is run");
            return "start";
        });
        CompletableFuture<String> futureEnd = CompletableFuture.supplyAsync(() -> {
            System.out.println("end is run");
            return "end";
        });

        List<String> collect = Stream.of(futureSatrt, futureEnd).map(f -> {
                return f.join();
        }).collect(Collectors.toList());

        collect.forEach(s-> System.out.println(s));
Copy the code

  1. Functional interface

ThenApply passes in a Function interface that returns the processed value. ThenApplyAsync needs to pass in a custom thread pool or forkJoin thread pool, and the rest does not change. The following consumer apis follow this rule, but I won’t go into details

ThenCompose receives a new functional interface of type CompletionStage that returns the processed value

        CompletableFuture<String> futureSatrt = CompletableFuture.supplyAsync(() -> {
            return "start";
        }).thenCompose(r->CompletableFuture.supplyAsync(()->{
            return r+"end"; })); System.out.println(futureSatrt.get());Copy the code

The difference between thenCompose and thenApply is that thenApply handles the same CompletableFuture as thenApply does, and thenCompose needs to pass in a new CompletableFuture, And the processed CompletableFuture is a brand new CompletableFuture

ThenCombine, you can use thenCombine to connect two different CompletableFutures and get them connected, return a new CompletableFuture, get the result with get, You can see that the new CompletableFuture returned by combining two CompletableFutures is not the same CompletableFuture, and the difference between thenAcceptBoth and thenAcceptBoth is that this interface returns a value, and thenAcceptBoth only consumes

CompletableFuture<String> futureSatrt = CompletableFuture.supplyAsync(() -> {
            return "start";
        });
        CompletableFuture<String> futureEnd = CompletableFuture.supplyAsync(() -> {
            return "end";
        });
        CompletableFuture<String> thenCombine = futureSatrt.thenCombine(futureEnd, (start, end) -> start + end);
        System.out.println(thenCombine.get());
        System.out.println(futureSatrt);
        System.out.println(futureEnd);
        System.out.println(thenCombine);
Copy the code

The handle interface type is a functional interface that passes in data and outliers and executes regardless of whenComplete, returning the processed value

        CompletableFuture<String> futureSatrt = CompletableFuture.supplyAsync(() -> {
            return "start";
        }).handle((t,e)->{
            System.out.println(e);
            return t+"end";
        });

        System.out.println(futureSatrt.get());
Copy the code

Another API of the same type as allof is anyof, which exits when a CompletableFuture runs out

        CompletableFuture<String> futureSatrt = CompletableFuture.supplyAsync(() -> {
            System.out.println("start is run");
            return "start";
        });
        CompletableFuture<String> futureEnd = CompletableFuture.supplyAsync(() -> {
            System.out.println("end is run");
            return "end";
        });

        Object o = CompletableFuture.anyOf(futureSatrt, futureEnd).get();

        System.out.println(o);
Copy the code

Exceptionally allows us to get executed exceptions and return custom values

        CompletableFuture<Object> end = CompletableFuture.supplyAsync(() -> {
            System.out.println("end is run");
            throw new RuntimeException();
        }).exceptionally((e) -> "error");

        System.out.println(end.get());
Copy the code

The above is just the basic API for CompletableFuture in Java8, and some of the API will be added in Java9, if you are interested, you can go down to learn about it

CompletableFuture principle please move


Youngitman. Tech / 2019/02/13 /…

References:

  1. Yq.aliyun.com/articles/71…
  2. www.cnblogs.com/txmfz/p/112…
  3. Youngitman. Tech / 2019/02/13 /…