CompletableFuture

578 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情

一、简介

什么是CompletableFuture?

在Java8中引入CompletableFuture用于异步编程,异步编程是多个线程同时运行,多个任务同时进行,与主线程隔离,并且会通知主线程它的进度,成功或者失败。该类实现了Future接口、CompletionStage接口

在这种方式中,主线程不会被阻塞,不需要一直等到子线程完成。主线程可以并行的执行其他任务。

使用这种并行方式,可以极大的提高程序的性能。

二、使用

(一)静态方法

CompletableFuture提供了如下五个静态方法,其中CompletableFuture.completedFuture是一个静态辅助方法,用来返回一个已经计算好的CompletableFuture。

剩余四个静态方法,其中两个参数为Runnable接口(无返回值),另外两个是Supplier接口(可以有返回值)。

public static <U> CompletableFuture<U> completedFuture(U value)

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
        return asyncSupplyStage(asyncPool, supplier);
    }

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
                                                       Executor executor) {
        return asyncSupplyStage(screenExecutor(executor), supplier);
    }

public static CompletableFuture<Void> runAsync(Runnable runnable) {
        return asyncRunStage(asyncPool, runnable);
    }

public static CompletableFuture<Void> runAsync(Runnable runnable,
                                                   Executor executor) {
        return asyncRunStage(screenExecutor(executor), runnable);
    }

没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。

如果指定线程池,则使用指定的线程池运行。

runAsync方法不支持返回值。supplyAsync可以支持返回值。

(二)异步任务完成时回调方法

CompletableFuture异步任务完成时,或者抛出异常时可以回调对应的方

/**
异步任务执行完成或者异步任务异常时回调方法
whenComplete:是执行当前任务的线程执行继续执行 whenComplete 的任务
**/
public CompletableFuture<T> whenComplete(
        BiConsumer<? super T, ? super Throwable> action) {
        return uniWhenCompleteStage(null, action);
    }
/**
whenComplete:是执行当前任务的线程把下一步的任务提交到线程池,由别的线程执行
**/
public CompletableFuture<T> whenCompleteAsync(
        BiConsumer<? super T, ? super Throwable> action) {
        return uniWhenCompleteStage(asyncPool, action);
    }
/**
exceptionally:异步任务出现异常时回调方法
**/
public CompletableFuture<T> exceptionally(
        Function<Throwable, ? extends T> fn) {
        return uniExceptionallyStage(fn);
    }

如下Demo

public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> s1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(5000);
                int i= 10/0;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "123";
                }
        ).whenComplete((s, throwable) -> { //s 是异步任务返回的结果
            System.out.println(s);
        }).exceptionally(x -> {
            System.out.println(x.toString);
            return "抛出异常结果";
        });

打印结果如下

null
java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero

(三)handle方法

下面一组方法虽然也返回CompletableFuture对象,但是对象的值和原来的CompletableFuture计算的值不同。当原先的CompletableFuture的值计算完成或者抛出异常的时候,会触发这个CompletableFuture对象的计算,结果由BiFunction参数计算而得。因此这组方法兼有whenComplete和转换的两个功能。

/**
同样,不以Async结尾的方法由原来的线程计算,以Async结尾的方法由默认的线程池ForkJoinPool.commonPool()或者指定的线程池executor运行。
**/
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)
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    return 100;
});
CompletableFuture<String> f =  future.thenApplyAsync(i -> i * 10).thenApply(i -> i.toString());
System.out.println(f.get()); //"1000"

(四)allOf 和 anyOf

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

allOf方法是当所有的CompletableFuture都执行完后执行计算。

anyOf方法是当任意一个CompletableFuture执行完后就会执行计算,计算的结果相同。

 public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> s1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("123");
            return "123";
                }
        );

        CompletableFuture<String> s2 = CompletableFuture.supplyAsync(() -> {
            //System.out.println("456");
                    return "456";
                }
        );

        CompletableFuture<String> s3 = CompletableFuture.supplyAsync(() -> {
            //System.out.println("789");
                    return "789";
                }
        );

        CompletableFuture aa = CompletableFuture.allOf(s1,s2,s3);
        aa.join(); //阻塞让三个线程都完成,再处理后续业务
        System.out.println("三个线程全部完成");

        CompletableFuture bb = CompletableFuture.anyOf(s1,s2,s3);
        System.out.println(bb.get());
    }

(五)组合方法

这一组方法接受一个Function作为参数,这个Function的输入是当前的CompletableFuture的计算值,返回结果将是一个新的CompletableFuture,这个新的CompletableFuture会组合原来的CompletableFuture和函数返回的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)
CompletableFuture<Object> s1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "123";
                }
        );

  CompletableFuture<String> f = s1.thenCompose(s -> {
            return CompletableFuture.supplyAsync(() -> {
                return s + "abc";
            });

        });
//返回结果时123abc

下面这组方法也是用来组合两个CompletableFuture结果,不同的是两个任务可以并行执行,不需要到等待别的任务执行成功后再执行

public <U,V> CompletableFuture<V> 	thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> 	thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> 	thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn, Executor executor)
        CompletableFuture<String> f = s1.thenCombine(
                CompletableFuture.supplyAsync(() -> {
                    System.out.println("789已经执行");
                            return "789";
                        }
                ), (o, s) -> {

                    return  o+"===="+s;
                });

        System.out.println(f.get());
    }
#会看到控制台上回先打印 789已经执行,并不会等s1执行完成后在执行第二个任务

三、总结

CompletableFuture是JDK8对Future能力的提升,使用它非常方便开启异步编程,并且异步任务结束后可自动执行回调方法,并且不再通过阻塞或轮询的方式获取异步任务的结果