,,Java8 CompletableFuture详解

,,Java8 CompletableFuture详解

本文主要介绍Java8 CompletableFuture的详细说明,它提供了方法、一元运算符和事件驱动的编程模型。有需要的可以参考一下。

Java到了,是时候学点新东西了。Java 7和Java 6只是稍微修改的版本,而Java 8会有很大的改进。可能Java 8太大了?今天,我将向您详细解释JDK 8中的新抽象——可完成的未来。众所周知,Java 8将在不到一年的时间内发布,因此本文基于支持lambda的JDK 8 build 88。CompleteFuture ExtendsFuture提供了方法、一元运算符和事件驱动的编程模型,并没有止步于旧版本的Java。如果你打开CompletableFuture的JavaDoc,你会大吃一惊。大概有五十种方式(!),还有一些很有意思,很难理解。比如复制代码如下:public u,v completablefuture thencombineasync(completablefuture?延伸你的另一面,双功能?超T,超级U,扩展V fn,遗嘱执行人遗嘱执行人不要担心,读下去。CompletableFuture集合了Guava和SettableFuture中ListenableFuture的所有特征。此外,内置的lambda表达式使其更接近Scala/Akka期货。这听起来好得令人难以置信,但请继续读下去。CompletableFuture在两个主要方面优于ol中的Future异步回调/转换,支持任何线程在任何时间设置CompletableFuture的值。

一、提取、修改包装的值

通常futures代表在其他线程中运行的代码,但情况并非总是如此。有时候你想创造一个未来来表明你知道会发生什么,比如JMS消息到达。所以你有未来,但是未来没有潜在的异步工作。您只想在将来JMS消息到达时简单地完成(解决)它,这是由事件驱动的。在这种情况下,您可以简单地创建CompletableFuture来返回给您的客户端。只要你认为你的结果是可用的,你只需通过complete()就可以解锁所有等待未来的客户端。

首先,你可以简单的创建一个新的CompletableFuture,交给你的客户端:复制代码如下:Public CompletableFuture Asks(){ Final CompletableFuture Future=New CompletableFuture();//.回报未来;}注意这个未来和Callable无关。没有线程池,也不能异步工作。现在如果客户端代码调用ask()。get(),它将永远阻塞。如果寄存器完成了回调,它们将永远不会生效。那么这有什么意义呢?现在你可以说:复制代码代码如下:future . complete(' 42 ')……这时所有的客户端Future.get()都会得到字符串的结果,回调完成后立即生效。当你想表示未来的任务的时候就很方便了,不需要计算一些执行线程的任务。CompletableFuture.complete()只能调用一次,后续调用将被忽略。但是也有一个后门叫做CompletableFuture。ObtrudeValue (…)覆盖了新未来之前的值。请小心使用。

有时候你希望看到信号失败。如您所知,Future对象可以处理它包含的结果或异常。如果想进一步传递一些异常,可以使用CompletableFuture。CompleteExceptionally)(或者使用更强大的方法,如obtrudeException(ex)来覆盖前面的异常)。CompleteExceptionally()也可以解锁所有等待的客户端,但是这一次从get()抛出了一个异常。说到get(),还有CompletableFuture.join()方法,在错误处理上略有变化。但总的来说,都是一样的。最后,还有CompletableFuture。GetNow (ValueIFABSENT)方法,该方法没有被阻塞,但如果未来没有完成,它将返回默认值,这使得它在构建我们不想等待太久的健壮系统时非常有用。

最后,静态方法是使用completedFuture(value)返回未来已经完成的对象,这在测试或编写一些适配器层时可能会非常有用。

二、创造和获取CompletableFuture

好,那么手动创建CompletableFuture是我们唯一的选择吗?不一定。就像一般的期货一样,我们可以关联已有的任务,而CompletableFuture使用工厂方法:复制代码如下:static u CompletableFuture supply async(supplier u supplier);static U CompletableFutureU supply sync(supplier U供应商,Executor执行者);静态CompletableFutureVoid run async(Runnable Runnable);静态CompletableFutureVoid run async(Runnable Runnable,Executor Executor);

无参数方法执行器以…Async结尾,将使用ForkJoinPool.commonPool()(全局,JDK8中引入的公共池),适用于CompletableFuture类中的大多数方法。RunAsync()很容易理解。注意它需要Runnable,所以它返回CompletableFutureVoid作为Runnable,不返回任何值。如果需要处理异步操作并返回结果,使用SupplierU:复制代码代码如下:Final Completable Future=Completable Future。supply sync(new supplier string(){ @ override public string get(){//.连续运行.返回“42”;}},执行人);

但是别忘了,Java 8里还有lambdas表达式!复制代码如下:FINALCOMPLETABLEFUTURESTING FUTURE=COMPLETABLEFUTURE。供应ASYC ()-{//.连续运行.返回“42”;},执行人);

或者:

复制代码如下:最终可完成期货期货=可完成期货。supply async(()-Longrunningtask(params),执行人);

虽然这篇文章不是关于lambda的,但是我会经常使用Lambda表达式。

三、转换和作用于CompletableFuture(thenApply)

我说CompletableFuture优于Future,但你不知道为什么吗?简单来说,因为CompletableFuture是原子,是因子。我说的没用吗?Scala和JavaScript都允许future在完成时注册异步回调。我们不必等到它准备好了再去阻止它。我们可以简单的说,运行这个函数,结果就出现了。此外,我们可以叠加这些功能,组合多个未来等。比如我们从String换成Integer,就可以从CompletableFuture换成CompletableFutureInteger,没有关联。这是通过thenApply()方法:复制代码代码如下:u Completable Futureu thenapply(函数?超T,扩展U fn);u CompletableFutureU thenApplyAsync(函数?超T,扩展U fn);u CompletableFutureU thenApplyAsync(函数?超T,extends U fn,Executor Executor);p/p

如前所述.异步版本提供了CompletableFuture的大部分操作,所以我将在后面的部分跳过它们。记住,第一个方法将在future完成的同一个线程中调用这个方法,而其余两个方法将在不同的线程池中异步调用它。让我们来看看Apply()的工作流程:/p

ppre class=' brush:Java;地沟:真;一线:1;高亮:[];html-script:false ' completablefuturestring f1=//.CompletableFutureInteger F2=f1 . then apply(Integer:parse int);CompletableFutureDouble F3=F2 . then apply(r-r * r * Math。PI);/p

或者在语句中:复制代码如下:CompleteFutureDouble F3=f1。然后应用(integer: Parseint)。然后应用(r-r * r * math . pi);

在这里,你会看到一个序列的转换,从字符串到整数再到双精度。但最重要的是,这些转换既不会立即执行,也不会立即停止。这些转换既不会立即执行,也不会停止。他们只是记得最初的f1完成了他们表演的节目。如果一些转换非常耗时,您可以提供自己的执行器来异步运行它们。注意,这个操作相当于Scala中的一元映射。

四、运行完成的代码(thenAccept/thenRun)复制代码如下:Completable FutureEvoid然后接受(消费者?超级T块);CompletableFutureVoid thenRun(可运行操作);

在未来的管道中有两种典型的“最终”阶段方法。它们是在你使用未来价值时准备好的。当thenAccept()提供最终值时,thenRun执行Runnable,后者甚至无法计算该值。比如复制代码如下:future . thenacceptsync(dbl-log . debug(' result:{ } ',dbl),executor);log . debug(“Continuing”);…异步变量也可以以两种方式使用,隐式和显式执行器。这个方法我就不太强调了。thenAccept()/thenRun()方法不会被阻塞(即使没有显式的执行器)。它们就像一个事件监听器/处理器。当你连接到未来,这将执行一段时间。“继续”信息会立即出现,即使未来还没有完成。

五、单个CompletableFuture的错误处理

到目前为止,我们只讨论了计算结果。异常点是怎么回事?我们能异步处理它们吗?当然啦!复制代码如下:CompletableFutureStringsafe=future . exceptual(ex-'我们有问题:' ex . getmessage());exception()在接受一个函数时会调用原来的future抛出异常。我们将有机会将此异常转换为一些与未来恢复类型兼容的值。safe的进一步转换将不再生成异常,而是从提供该函数的函数返回一个字符串值。更灵活的方法是handle()接受一个函数,该函数接收正确的结果或异常:复制代码code如下:Completable future integer Safe=future . handle((ok,ex)-{if (ok!=null){ return integer . parse int(ok);} else { log.warn('问题',ex);return-1;}});Handle()始终被调用,结果和异常不为空。这是一个一站式的全方位战略。

六、一起结合两个CompletableFuture

异步过程之一的CompletableFuture非常好,但是当多个这样的未来以各种方式组合在一起时,它才真正显示出它的威力。

七、结合(链接)这两个futures(thenCompose())

有时候你想运行一些future的值(当它准备好的时候),但是这个函数也返回future。CompletableFuture足够灵活,可以理解我们函数的结果现在应该是顶级的Future,与CompletableFuture CompletableFuture相比。方法thenCompose()相当于Scala的flatMap:复制代码如下:u completablefutureu thencompose(函数?super T,CompletableFutureU fn);…异步更改也可用。在下面的例子中,仔细观察Apply()(map)和thenCompose()(flatMap)的类型和区别。当应用calculateRelevance()方法时,它返回CompletableFuture:

复制代码如下:可完成的未来单据Doc Future=//.

CompletableFutureCompletableFutureDouble f=doc future . then apply(this:calculateRelevance);

CompletableFutureDouble relevance future=doc future . then compose(this:calculateRelevance);

//.

私有CompletableFutureDouble calculateRelevance(文档doc) //.

ThenCompose()是一个重要的方法,它允许构建健壮的异步管道,没有阻塞和等待的中间步骤。

八、两个futures的转换值(thenCombine())

当使用thenCompose()链接一个未来时,它依赖于另一个thenCombine,当它们都完成时,它组合两个独立的未来:复制代码如下:U,V Completable Future ev Then Combine(Completable Future?延伸你的另一面,双功能?超T,超级U,扩展V fn)…异步变量也是可用的。假设您有两个CompletableFuture,一个装载客户,另一个装载最近的商店。它们彼此完全独立,但是当它们完成时,你要用它们的值来计算路线。这是一个剥夺的例子:

复制代码如下:Completable Future Customer Future=LoadCustomerDetails(123);CompletableFutureShop shop future=closestShop();CompletableFutureRoute route future=customer future . then combine(shop future,(cust,shop) - findRoute(cust,shop));

//.

私人路线查找路线(顾客顾客,商店商店)//.请注意(cust,shop)-findRoute(cust,shop)简单替换this:findRoute方法的引用:复制代码代码如下:customer future . then combine(Shop future,this:find route);

如你所知,我们有顾客未来和商店未来。然后routeFuture包装它们,然后“等待”它们完成。当它们准备好时,它将运行我们提供的函数来合并所有结果(findRoute())。当两个基本Future完成并且findRoute()也完成时,那么routeFuture也将完成。

九、等待所有的 CompletableFutures 完成

如果我们不生成一个新的CompletableFuture来连接这两个结果,我们只想在完成时得到通知。我们可以使用thenAcceptBoth()/runAfterBoth()系列的方法,(…Async变量也可用)。它们的工作方式与thenAccept()和thenRun()类似,只是等待两个未来而不是一个:复制代码如下:u COMPLETABLE FUTURE oid then accept both(COMPLETABLE FUTURE?延伸出另一个,双消费者?超T,超级U block)CompletableFutureVoid runAfterBoth(CompletableFuture?其他可运行的操作)

想象一下上面的例子。这不是为了产生一个新的CompletableFuture。您只想发送一些事件或立即刷新GUI。这可以很容易地实现:

复制代码如下:customerfuture。thenaceptblue(shop future,(cust,shop)-{ final route=find route(cust,shop);//用route}刷新GUI

我希望我是错的,但也许有人会问自己一个问题:为什么我不能简单地屏蔽这两个未来?就像:复制代码代码如下:Future Customer Customer Future=LoadCustomerDetails(123);FutureShop shop future=closest shop();findRoute(customerFuture.get()、shop future . get());

你当然可以这么做。但关键是CompletableFuture是异步的,它是事件驱动的编程模型,而不是阻塞和急切等待结果。所以上面两部分代码在功能上是等价的,只是后者不需要占用一个线程来执行。

十、等待第一个 CompletableFuture 来完成任务

另一个有趣的事情是CompletableFutureAPI可以等待第一个(与所有相反)完成的未来。当你有两个同类型任务的结果时,就非常方便了。你所要做的就是关心响应时间。没有任务有优先权。API(…异步变量也是可用的):

复制代码如下:Completable Future Evoid接受任一(Completable Future?延伸到其他,消费者?超级T块)CompletableFutureVoid runafternoir(CompletableFuture?其他可运行的操作)

例如,您有两个可以集成的系统。一种是平均响应时间短,但标准偏差高,另一种通常较慢,但更容易预测。为了两全其美(性能和可预测性),可以同时调用两个系统,等待谁先完成。通常这将是第一个系统,但是当进度变得缓慢时,第二个系统可以在可接受的时间内完成:

复制代码如下:CompletableFutureStringfast=fetch fast();CompletableFutureString predictable=fetch predictable();fast.acceptEither要么(可预测,s-{ system . out . println(' Result:' s ');});

s表示从fetchFast()或fetchPredictably()获得的字符串。我们不需要知道或者关心。

十一、完整地转换第一个系统

ApplyToEither()是acceptEither()的前身。当两个未来快完成的时候,后者只是简单的调用一些代码片段,applyToEither()就会返回一个新的未来。当这两个最初的未来完成时,新的未来也将完成。该API有点类似于(…异步变量也是可用的):

复制代码如下:u COMPLETABLEFUTURE APPLY to ither(COMPLETABLEFUTURE?扩展其他函数?t,u fn)这个额外的fn函数可以在调用第一个future时完成。我不确定这个特殊化方法的目的是什么。毕竟,人们可以简单地使用它:fast . applytoor(predictive)。然后应用(fn)。因为我们坚持要用这个API,但是真的不需要有额外函数的应用,我就简单用Function.identity()占位符:复制代码如下:Completable FutureStringFast=fetch fast();CompletableFutureString predictable=fetch predictable();CompletableFutureString first done=fast . apply toner(可预测,函数。stringid entity());第一个完成的期货可以由。请注意,从客户的角度来看,这两个期货实际上隐藏在firstDone后面。客户端只需等待未来完成,并在前两个任务完成时通过applyToEither()通知客户端。

十二、多种结合的CompletableFuture

现在我们知道如何等待两个future完成(使用thenCombine())和先完成(applyToEither())。但是可以推广到任意数量的期货吗?的确,使用静态辅助的方法:复制代码代码如下:静态Completable Future进化了所有的(Completable Future?cfs)静态CompletableFutureObject any of(CompletableFuture?cfs)allOf()当所有潜在的未来完成时,使用一个未来数组,并返回一个未来(等待所有障碍)。另一方面,anyOf()将等待最快的潜在未来。请看一下一般类型的返回期货。这不正是你所期望的吗?我们将在下一篇文章中关注这个问题。

总结

我们探索了整个CompletableFuture API。我相信它会战无不胜,所以在下一篇文章中,我们将研究另一个简单网络爬虫的实现,使用CompletableFuture方法和Java 8 lambda表达式,我们还将研究CompletableFuture的

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: