java8-[CompletableFuture]

来源:互联网 发布:网络宣传怎么做 编辑:程序博客网 时间:2024/05/23 10:50

Future代表异步执行的挂起的结果。它的方法get,在执行计算后返回执行结果。由此产生问题:get方法调用是阻塞的,直到异步计算结束。由此造成异步计算起不到“异步”的效果。你可以在job中实现所有场景,再发送到executor中,但是为什么要为错综复杂的逻辑编写同样错综复杂的执行步骤呢。因此,你需要CompletableFuture

CompletableFuture

实现Future接口外,它还实现了CompletionStage接口,CompletionStage接口保证了计算最终会被执行。CompletionStage的最大优点是,提供了多种方法,用于执行结束时回调(callbacks)。由此构建非阻塞的系统。

最简单的异步计算

创建异步计算:
CompletableFuture.supplyAsync(this::sendMsg);
supplyAsync方法有一个Supplier类作为参数,该类包含了需要异步执行的代码,这个例子中是sendMsg方法。像Future一样,可以提供Executor作为第二个参数,如果缺省,Supplier会被提交给ForkJoinPool.commonPool()

添加回调

回调的意义是:当异步计算结束时,执行回调的代码,而不用一直等待执行结束。上例中,通过sendMsg方法异步发送一个消息,接下来,我们添加回调代码,用于通知发送消息执行的结果。

CompletableFuture.supplyAsync(this::sendMsg).thenAccept(this::notify);

thenAccept是诸多添加回调的一种方法。参数是Consumer,本例中是notify,用于计算执行结束时处理结果。

级联多个回调

如果想持续的在多个回调之间传递值,thenAccept不能用,因为Consumer不返回任何东西。一直传值,可以用thenApply
thenApply的参数Function,接受一个值,并且返回一个值。

CompletableFuture.supplyAsync(this::findReceiver).thenApply(this:sendMsg).thenAccept(this::notify);

现在异步的任务是,首先找到接收者,然后发送消息到接收器,最后发送消息的结果发给notify。

构建异步系统

构建更大的异步系统,你很可能想创建新的代码段,依赖小的代码段。这些大小代码段都是异步的,在我们的例子中返回CompletionStage。到目前为止,sendMsg都是一个阻塞函数,假设有个返回CompletionStage的sendMsgAsync函数,如果像上面例子一直使用thenApply,最终我们会得到一堆嵌套的CompletionStage。

CompletableFuture.supplyAsync(this::findReceiver).thenApply(this::sendMsgAsnc);// returns type: CompletionStage<CompletionStage<String>>

而我们不想要嵌套的CompletionStage,所以我们可以用thenCompose,它接受Function参数,返回CompletionStage。就像flatMap实现的扁平效果一样:

CompletableFuture.supplyAsync(this::findReceiver).thenCompose(this::sendMsgAsync);// returns type CompletionStage<String>

用async结尾的回调分离的任务

到目前为止,所有的回调都在它前一个执行单元的线程中执行。可以把回调各自提交到ForkJoinPool.commonPool(),而不是使用前一个执行单元的线程,这是通过使用CompletionStage提供的以async后缀的方法实现的。
发送两个报文到相同的接收器:

CompletableFuture<String> reciever = CompletableFuture.supplyAsync(this::findReceiver);reciever.thenApply(this::sendMsg);reciever.thenApply(this::sendOtherMsg);

上面例子中,所有的Function都在同一个线程中执行,导致后一个消息等待前一个消息执行完。

CompletableFuture<String> reciever = CompletableFuture.supplyAsync(this::findReciever);receiver.thenApplyAsync(this::sendMsg);reciever.thenApplyAsync(this::sendMsg);

上面的例子,每个发送消息都被独立的提交给ForkJoinPool.commonPool()。结果是两个sendMsg回调在异步计算执行结束时都会执行。
关键是:异步版本的方法,在你有几个回调函数,而它们又依赖相同计算结果的时,非常方便有效。

异常的处理

如果你用过Future,就会知道糟糕的时候有多糟糕。幸运的是,CompletableFuture有一个漂亮的对应手段,通过使用exceptionally

CompletableFuture.supplyAsync(this::failingMsg).exceptionally(ex->new Result(Status.FAILED)).thenAccept(this::notify);

exceptionally给我们一个机会恢复,通过执行当异步执行的计算抛出exception时备选的方法(alternative method)。而其他的回调可以继续执行,它们的输入是alternative method的返回值。更复杂的差错处理,可以用whenCompletehandle

基于多个计算结果的回调

创建依赖多个计算结果的回调,用thenCombine,它允许注册BiFunction类回调,依赖两个CompletionStage

CompletableFuture<String> to = CompletableFuture.supplyAsync(this::findReceiver);CompletableFuture<String> text = CompletableFuture.supplyAsync(this::createContent);to.thenCombine(text, this::sendMsg);

值得一提的是,thenCombine有一个变种,runAfterBoth,它不关心前面的计算结果,只要它们执行完就够了,它有一个Runnable参数。

依赖于至少一个计算结果的回调

发送消息有两个消息源,任何一个源执行完毕就可以发送消息。

CompletableFuture<String> firstSource = CompletableFuture.supplyAsync(this::findByFirstSource);CompletableFuture<String> secondSource = CompletableFuture.supplyAsync(this::findBySecondSource);firstSource.acceptEither(secondSource, this::sendMsg);

通过acceptEither实现,它接受两个等待中的计算,以及一个Consumer,后者在两个计算中任意一个执行结束后执行。

例子

@Scheduled(fixedRate = 2000)public void test() {    log.info("begin: {}", new Date());    ActionService actionService = new ActionService();    CompletableFuture<Void> future = CompletableFuture.supplyAsync(actionService::test1)        .thenApply(actionService::test2)        .thenApply(actionService::test3)        .thenAccept(actionService::test4);    try {        future.get();    } catch (InterruptedException e) {        e.printStackTrace();    } catch (ExecutionException e) {        e.printStackTrace();    }    log.info("end: {}", new Date());}public Integer test1() {    log.info("{}, {}", Thread.currentThread().getName(), new Date());    try {        Thread.sleep(1000l);    } catch (InterruptedException e) {        log.error(e.getMessage());    }    return 1;}

运行结果:
执行结果

CompletableFuture的更多用法

见document

原创粉丝点击