一、简介
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()中处理异常,并返回一个默认的值