一、简介

  CompletableFuture是Java 8新增的一个超大型工具类。一方面,它实现了Future接口,而更重要的是,它也实现了CompletionStage接口。CompletionStage接口也是在Java 8中新增的。而CompletionStage接口拥有多达约40种方法,这个接口之所以拥有如此众多的方法,是为了函数式编程中的流式调用准备的。通过CompletionStage提供的接口,我们可以在一个执行结果上进行多次流式调用,以此可以得到最终结果

二、完成了就通知我

CompletableFuture和Future一样,可以作为函数调用的契约。如果你向CompletableFuture请求一个数据,如果数据还没有准备好,请求线程就会等待。而让人惊喜的是,通过CompletableFuture,我们可以手动设置CompletableFuture的完成状态。

public class CompletableFutureTest implements Runnable {
    CompletableFuture<Integer> re = null;
    public CompletableFutureTest(CompletableFuture<Integer> re) {
        this.re = re;
    }
    
    @Override
    public void run() {
        int myre = 0;
        try {
            myre = re.get() * re.get();
        } catch(Exception ex) {
            
        }
        System.out.println(myre);
    }
    
    public static void main(String[] args) throws InterruptedException {
        CompletableFuture<Integer> re = new CompletableFuture<Integer>();
        new Thread(new CompletableFutureTest(re)).start();
        Thread.sleep(1000);
        //告知完成结果
        re.complete(3);
    }
}

打印:9

三、异步执行任务

    public static Integer calc(Integer para) {
        try {
            Thread.sleep(1000);
        } catch(InterruptedException e) {
            
        }
        return para*para;
    }
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        CompletableFuture<Integer> re = CompletableFuture.supplyAsync(new Supplier<Integer>() {
            @Override
            public Integer get() {
                System.out.println(Thread.currentThread().getId() + "get");
                return calc(50);
            }
        });
        //告知完成结果
        System.out.println(Thread.currentThread().getId() + "main");
        System.out.println(re.get());
    }

执行结果:

1main
10get
2500

CompletableFuture.supplyAsync()方法构造一个CompletableFuture实例,在supplyAsync()函数中,它会在一个新的线程中,执行传入的参数。在这里,它会执行calc()方法。而calc()方法的执行可能是比较慢的,但是这不影响CompletableFuture实例的构造速度,因此supplyAsync()会立即返回,它返回的CompletableFuture对象实例就可以作为这次调用的契约,在将来任何场合,用于获得最终的计算结果。代码第13行,试图获得calc()的计算结果,如果当前计算没有完成,则调用get()方法的线程就会等待。

在CompletableFuture中,类似的工厂方法有以下几个:

static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor);
static CompletableFuture<Void> runAsync(Runnable runnable);
static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);

其中supplyAsync()方法用于那些需要有返回值的场景,比如计算某个数据等。而runAsync()方法用于没有返回值的场景,比如,仅仅是简单地执行某一个异步动作。在这两对方法中,都有一个方法可以接收一个Executor参数。这就使我们可以让Supplier <U>或者Runnable在指定的线程池中工作。如果不指定,则在默认的系统公共的ForkJoinPool.common线程池中执行。

注意:在Java 8中,新增了ForkJoinPool.commonPool()方法。它可以获得一个公共的ForkJoin线程池。这个公共线程池中的所有线程都是Daemon线程。这意味着如果主线程退出,这些线程无论是否执行完毕,都会退出系统。

四、流式调用

    public static Integer calc(Integer para) {
        try {
            Thread.sleep(1000);
        } catch(InterruptedException e) {
            
        }
        return para*para;
    }
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        CompletableFuture<Void> re = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getId() + "get");
            return calc(50);
        }).thenApply(new Function<Integer, String>() {
            @Override
            public String apply(Integer i) {
                System.out.println(Thread.currentThread().getId() + "thenApply");
                return Integer.toString(i);
            }
        }).thenAccept(new Consumer<String>() {
            @Override
            public void accept(String t) {
                System.out.println(Thread.currentThread().getId() + "thenAccept");
                System.out.println(t);
            }
        });
        //告知完成结果
        System.out.println(Thread.currentThread().getId() + "main");
        System.out.println("final get" + re.get());
    }

执行结果

10get
1main
10thenApply
10thenAccept
2500
final getnull

五、异常处理

如果CompletableFuture在执行过程中遇到异常,我们可以用函数式编程的风格来优雅地处理这些异常。CompletableFuture提供了一个异常处理方法exceptionally():

    public static Integer calc(Integer para) {
        int t = 100/0;
        return para*para;
    }
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        CompletableFuture<Void> re = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getId() + "get");
            return calc(50);
        }).exceptionally(new Function<Throwable, Integer>() {
            @Override
            public Integer apply(Throwable t) {
                System.out.println(t.getMessage());
                return 0;
            }
        }).thenApply(new Function<Integer, String>() {
            @Override
            public String apply(Integer i) {
                System.out.println(Thread.currentThread().getId() + "thenApply");
                return Integer.toString(i);
            }
        }).thenAccept(new Consumer<String>() {
            @Override
            public void accept(String t) {
                System.out.println(Thread.currentThread().getId() + "thenAccept");
                System.out.println(t);
            }
        });
        //告知完成结果
        System.out.println(Thread.currentThread().getId() + "main");
        System.out.println("final get" + re.get());
    }

执行结果:

10get
java.lang.ArithmeticException: / by zero
1thenApply
1thenAccept
0
1main
final getnull

在上述代码中,第8行对当前的CompletableFuture进行异常处理。如果没有异常发生,则CompletableFuture就会返回原有的结果。如果遇到了异常,就可以在exceptionally()中处理异常,并返回一个默认的值