Java8新的异步编程方式 CompletableFuture(一)

    |     2018年2月9日   |   java8新特性   |     0 条评论   |    952

JDK 5 引入 Future,把「把任务交出去、过会儿再取结果」写成接口;Java 8 的 CompletableFuture 补上回调和组合,才算有完整的 Future / Promise / Callback。

Future 模式:我有任务交给 Future,期间我可以干别的,稍后再 get。接口只有五个方法。

一、Future

public interface Future<V> {

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)

        throws InterruptedException, ExecutionException, TimeoutException;

}
  • cancel(boolean):取消;参数决定是否立刻中断。
  • isCancelled():正常完成前被取消则 true。
  • isDone():正常结束、异常或取消都算完成。
  • get():阻塞等到结果;中断、执行异常、取消会抛相应异常。
  • get(timeout, unit):带超时,超时抛 TimeoutException。

一般和 Callable 一起用:ExecutorService.submit 返回 Future。

ExecutorService executor = Executors.newCachedThreadPool();

        Future<String> future = executor.submit(() -> { //Lambda 是一个 callable, 提交后便立即执行,这里返回的是 FutureTask 实例

            System.out.println("running task");

            Thread.sleep(10000);

            return "return task";

        });

        try {

            Thread.sleep(1000);

        } catch (InterruptedException e) {

        }

        System.out.println("do something else");  //前面的的 Callable 在其他线程中运行着,可以做一些其他的事情

        try {

            System.out.println(future.get());  //等待 future 的执行结果,执行完毕之后打印出来

        } catch (InterruptedException e) {

        } catch (ExecutionException e) {

        } finally {

            executor.shutdown();

        }

比起裸 get(),更推荐带超时的 get(long, TimeUnit),避免无限等。

二、Future 的短板与 CompletableFuture

  • 能取异步结果,但没有完成通知。
  • 要么 get() 阻塞变回同步,要么 isDone() 轮询耗 CPU。

Netty、Guava 都扩展过 Future。Java 8 的 CompletableFuture 吸收了 Guava ListenableFuture / SettableFuture,并把回调放到别的线程,或当作同一线程里的同步继续。任务完成后要用结果,不必干等,可 thenAccept / thenApply / thenCompose 交给下一段。

三、静态工厂:runAsync 与 supplyAsync

completablefutrue

runAsync 返回的 CompletableFuture 没有业务返回值;supplyAsync 有。下面两段分别对应这两种工厂:

System.out.println("Hello");

        });

        try {

            future.get();

        } catch (InterruptedException e) {

            e.printStackTrace();

        } catch (ExecutionException e) {

            e.printStackTrace();

        }

        System.out.println("CompletableFuture");
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");

        try {

            System.out.println(future.get());

        } catch (InterruptedException e) {

            e.printStackTrace();

        } catch (ExecutionException e) {

            e.printStackTrace();

        }

        System.out.println("CompletableFuture");

四、complete 与 completeExceptionally

方法 含义
complete(T t) 完成异步执行,并给出结果
completeExceptionally(Throwable) 以异常结束

get() 会一直 block;若此时 complete,会立刻以给定值结束。只能成功一次,后续重复调用无效。

CompletableFuture<String> future  = CompletableFuture.supplyAsync(() -> "Hello");

        future.complete("World");

        try {

            System.out.println(future.get());

        } catch (InterruptedException e) {

            e.printStackTrace();

        } catch (ExecutionException e) {

            e.printStackTrace();

        }

执行结果:

World

若 Future 已经能返回结果,再 complete 就无效:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");

        try {

            Thread.sleep(5000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        future.complete("World");

        try {

            System.out.println(future.get());

        } catch (InterruptedException e) {

            e.printStackTrace();

        } catch (ExecutionException e) {

            e.printStackTrace();

        }

执行结果:

Hello

completeExceptionally 则让 get 看到异常而不是成功值:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");

        future.completeExceptionally(new Exception());

        try {

            System.out.println(future.get());

        } catch (InterruptedException e) {

            e.printStackTrace();

        } catch (ExecutionException e) {

            e.printStackTrace();

        }

执行结果:

java.util.concurrent.ExecutionException: java.lang.Exception

...

原文系列:CompletableFuture(一)、(二)、(三)。

一句话总结:Future 只能提交再阻塞/轮询取结果;CompletableFuture 用工厂方法启动任务,再用 complete 或回调链把「完成后干什么」接上,才是 Java 8 的异步组合方式。

转载请注明来源:Java8新的异步编程方式 CompletableFuture(一)
本文链接地址:https://ai.zhousir.top/?p=2241
回复 取消