应用办公生活信息教育商业
投稿投诉
商业财经
汽车智能
教育国际
房产环球
信息数码
热点科技
生活手机
晨报新闻
办公软件
科学动态
应用生物
体育时事

代替Future的CompletableFuture让你的代

  通过阅读本篇文章你将了解到:CompletableFuture的使用CompletableFure异步和同步的性能测试已经有了Future为什么仍需要在JDK1。8中引入CompletableFutureCompletableFuture的应用场景对CompletableFuture的使用优化场景说明
  查询所有商店某个商品的价格并返回,并且查询商店某个商品的价格的API为同步一个Shop类,提供一个名为getPrice的同步方法店铺类:Shop。javapublicclassShop{privateRandomrandomnewRandom();根据产品名查找价格publicdoublegetPrice(Stringproduct){returncalculatePrice(product);}计算价格paramproductreturnprivatedoublecalculatePrice(Stringproduct){delay();random。nextDouble()随机返回折扣returnrandom。nextDouble()product。charAt(0)product。charAt(1);}通过睡眠模拟其他耗时操作privatevoiddelay(){try{Thread。sleep(1000);}catch(InterruptedExceptione){e。printStackTrace();}}}
  查询商品的价格为同步方法,并通过sleep方法模拟其他操作。这个场景模拟了当需要调用第三方API,但第三方提供的是同步API,在无法修改第三方API时如何设计代码调用提高应用的性能和吞吐量,这时候可以使用CompletableFuture类CompletableFuture使用
  Completable是Future接口的实现类,在JDK1。8中引入CompletableFuture的创建:说明:两个重载方法之间的区别后者可以传入自定义Executor,前者是默认的,使用的ForkJoinPoolsupplyAsync和runAsync方法之间的区别前者有返回值,后者无返回值Supplier是函数式接口,因此该方法需要传入该接口的实现类,追踪源码会发现在run方法中会调用该接口的方法。因此使用该方法创建CompletableFuture对象只需重写Supplier中的get方法,在get方法中定义任务即可。又因为函数式接口可以使用Lambda表达式,和new创建CompletableFuture对象相比代码会简洁不少使用new方法CompletableFutureDoublefuturePricenewCompletableFuture();使用CompletableFuturecompletedFuture静态方法创建publicstaticUCompletableFutureUcompletedFuture(Uvalue){returnnewCompletableFutureU((valuenull)?NIL:value);}参数的值为任务执行完的结果,一般该方法在实际应用中较少应用使用CompletableFuturesupplyAsync静态方法创建supplyAsync有两个重载方法:方法一publicstaticUCompletableFutureUsupplyAsync(SupplierUsupplier){returnasyncSupplyStage(asyncPool,supplier);}方法二publicstaticUCompletableFutureUsupplyAsync(SupplierUsupplier,Executorexecutor){returnasyncSupplyStage(screenExecutor(executor),supplier);}使用CompletableFuturerunAsync静态方法创建runAsync有两个重载方法方法一publicstaticCompletableFutureVoidrunAsync(Runnablerunnable){returnasyncRunStage(asyncPool,runnable);}方法二publicstaticCompletableFutureVoidrunAsync(Runnablerunnable,Executorexecutor){returnasyncRunStage(screenExecutor(executor),runnable);}结果的获取:对于结果的获取CompltableFuture类提供了四种方式方式一publicTget()方式二publicTget(longtimeout,TimeUnitunit)方式三publicTgetNow(TvalueIfAbsent)方式四publicTjoin()
  说明:
  示例:get()和get(longtimeout,TimeUnitunit)在Future中就已经提供了,后者提供超时处理,如果在指定时间内未获取结果将抛出超时异常getNow立即获取结果不阻塞,结果计算已完成将返回结果或计算过程中的异常,如果未计算完成将返回设定的valueIfAbsent值join方法里不会抛出异常publicclassAcquireResultTest{publicstaticvoidmain(String〔〕args)throwsExecutionException,InterruptedException{getNow方法测试CompletableFutureStringcp1CompletableFuture。supplyAsync((){try{Thread。sleep(60100060);}catch(InterruptedExceptione){e。printStackTrace();}returnhelloworld;});System。out。println(cp1。getNow(helloh2t));join方法测试CompletableFutureIntegercp2CompletableFuture。supplyAsync((()10));System。out。println(cp2。join());get方法测试CompletableFutureIntegercp3CompletableFuture。supplyAsync((()10));System。out。println(cp3。get());}}
  说明:第一个执行结果为helloh2t,因为要先睡上1分钟结果不能立即获取join方法获取结果方法里不会抛异常,但是执行结果会抛异常,抛出的异常为CompletionExceptionget方法获取结果方法里将抛出异常,执行结果抛出的异常为ExecutionException异常处理:使用静态方法创建的CompletableFuture对象无需显示处理异常,使用new创建的对象需要调用completeExceptionally方法设置捕获到的异常,举例说明:CompletableFuturecompletableFuturenewCompletableFuture();newThread((){try{doSomething,调用complete方法将其他方法的执行结果记录在completableFuture对象中completableFuture。complete(null);}catch(Exceptione){异常处理completableFuture。completeExceptionally(e);}})。start();同步方法Pick异步方法查询所有店铺某个商品价格
  店铺为一个列表:privatestaticListShopshopListArrays。asList(newShop(BestPrice),newShop(LetsSaveBig),newShop(MyFavoriteShop),newShop(BuyItAll));
  同步方法:privatestaticListStringfindPriceSync(Stringproduct){returnshopList。stream()。map(shopString。format(spriceis。2f,shop。getName(),shop。getPrice(product)))格式转换。collect(Collectors。toList());}
  异步方法:privatestaticListStringfindPriceAsync(Stringproduct){ListCompletableFutureStringcompletableFutureListshopList。stream()转异步执行。map(shopCompletableFuture。supplyAsync(()String。format(spriceis。2f,shop。getName(),shop。getPrice(product))))格式转换。collect(Collectors。toList());returncompletableFutureList。stream()。map(CompletableFuture::join)获取结果不会抛出异常。collect(Collectors。toList());}
  性能测试结果:FindPriceSyncDonein4141FindPriceAsyncDonein1033
  异步执行效率提高四倍为什么仍需要CompletableFuture
  在JDK1。8以前,通过调用线程池的submit方法可以让任务以异步的方式运行,该方法会返回一个Future对象,通过调用get方法获取异步执行的结果:privatestaticListStringfindPriceFutureAsync(Stringproduct){ExecutorServiceesExecutors。newCachedThreadPool();ListFutureStringfutureListshopList。stream()。map(shopes。submit(()String。format(spriceis。2f,shop。getName(),shop。getPrice(product))))。collect(Collectors。toList());returnfutureList。stream()。map(f{Stringresultnull;try{resultf。get();}catch(InterruptedExceptione){e。printStackTrace();}catch(ExecutionExceptione){e。printStackTrace();}returnresult;})。collect(Collectors。toList());}
  既生瑜何生亮,为什么仍需要引入CompletableFuture?对于简单的业务场景使用Future完全没有,但是想将多个异步任务的计算结果组合起来,后一个异步任务的计算结果需要前一个异步任务的值等等,使用Future提供的那点API就囊中羞涩,处理起来不够优雅,这时候还是让CompletableFuture以声明式的方式优雅的处理这些需求。而且在Future编程中想要拿到Future的值然后拿这个值去做后续的计算任务,只能通过轮询的方式去判断任务是否完成这样非常占CPU并且代码也不优雅,用伪代码表示如下:while(future。isDone()){resultfuture。get();doSomrthingWithResult(result);}
  但CompletableFuture提供了API帮助我们实现这样的需求其他API介绍whenComplete计算结果的处理:
  对前面计算结果进行处理,无法返回新值提供了三个方法:方法一publicCompletableFutureTwhenComplete(BiConsumerlt;?superT,?superThrowableaction)方法二publicCompletableFutureTwhenCompleteAsync(BiConsumerlt;?superT,?superThrowableaction)方法三publicCompletableFutureTwhenCompleteAsync(BiConsumerlt;?superT,?superThrowableaction,Executorexecutor)
  说明:BiFunctionlt;?superT,?superU,?extendsVfn参数定义对结果的处理Executorexecutor参数自定义线程池以async结尾的方法将会在一个新的线程中执行组合操作
  示例:publicclassWhenCompleteTest{publicstaticvoidmain(String〔〕args){CompletableFutureStringcf1CompletableFuture。supplyAsync(()hello);CompletableFutureStringcf2cf1。whenComplete((v,e)System。out。println(String。format(value:s,exception:s,v,e)));System。out。println(cf2。join());}}thenApply转换:
  将前面计算结果的的CompletableFuture传递给thenApply,返回thenApply处理后的结果。可以认为通过thenApply方法实现CompletableFuture至CompletableFuture的转换。白话一点就是将CompletableFuture的计算结果作为thenApply方法的参数,返回thenApply方法处理后的结果提供了三个方法:方法一publicUCompletableFutureUthenApply(Functionlt;?superT,?extendsUfn){returnuniApplyStage(null,fn);}方法二publicUCompletableFutureUthenApplyAsync(Functionlt;?superT,?extendsUfn){returnuniApplyStage(asyncPool,fn);}方法三publicUCompletableFutureUthenApplyAsync(Functionlt;?superT,?extendsUfn,Executorexecutor){returnuniApplyStage(screenExecutor(executor),fn);}
  说明:Functionlt;?superT,?extendsUfn参数对前一个CompletableFuture计算结果的转化操作Executorexecutor参数自定义线程池以async结尾的方法将会在一个新的线程中执行组合操作示例:publicclassThenApplyTest{publicstaticvoidmain(String〔〕args)throwsExecutionException,InterruptedException{CompletableFutureIntegerresultCompletableFuture。supplyAsync(ThenApplyTest::randomInteger)。thenApply((i)i8);System。out。println(result。get());}publicstaticIntegerrandomInteger(){return10;}}
  这里将前一个CompletableFuture计算出来的结果扩大八倍thenAccept结果处理:
  thenApply也可以归类为对结果的处理,thenAccept和thenApply的区别就是没有返回值提供了三个方法:方法一publicCompletableFutureVoidthenAccept(Consumerlt;?superTaction){returnuniAcceptStage(null,action);}方法二publicCompletableFutureVoidthenAcceptAsync(Consumerlt;?superTaction){returnuniAcceptStage(asyncPool,action);}方法三publicCompletableFutureVoidthenAcceptAsync(Consumerlt;?superTaction,Executorexecutor){returnuniAcceptStage(screenExecutor(executor),action);}
  说明:Consumerlt;?superTaction参数对前一个CompletableFuture计算结果的操作Executorexecutor参数自定义线程池同理以async结尾的方法将会在一个新的线程中执行组合操作示例:publicclassThenAcceptTest{publicstaticvoidmain(String〔〕args){CompletableFuture。supplyAsync(ThenAcceptTest::getList)。thenAccept(strListstrList。stream()。forEach(mSystem。out。println(m)));}publicstaticListStringgetList(){returnArrays。asList(a,b,c);}}
  将前一个CompletableFuture计算出来的结果打印出来thenCompose异步结果流水化:
  thenCompose方法可以将两个异步操作进行流水操作提供了三个方法:方法一publicUCompletableFutureUthenCompose(Functionlt;?superT,?extendsCompletionStageUfn){returnuniComposeStage(null,fn);}方法二publicUCompletableFutureUthenComposeAsync(Functionlt;?superT,?extendsCompletionStageUfn){returnuniComposeStage(asyncPool,fn);}方法三publicUCompletableFutureUthenComposeAsync(Functionlt;?superT,?extendsCompletionStageUfn,Executorexecutor){returnuniComposeStage(screenExecutor(executor),fn);}
  说明:Functionlt;?superT,?extendsCompletionStagefn参数当前CompletableFuture计算结果的执行Executorexecutor参数自定义线程池同理以async结尾的方法将会在一个新的线程中执行组合操作示例:publicclassThenComposeTest{publicstaticvoidmain(String〔〕args)throwsExecutionException,InterruptedException{CompletableFutureIntegerresultCompletableFuture。supplyAsync(ThenComposeTest::getInteger)。thenCompose(iCompletableFuture。supplyAsync(()i10));System。out。println(result。get());}privatestaticintgetInteger(){return666;}privatestaticintexpandValue(intnum){returnnum10;}}
  执行流程图:
  thenCombine组合结果:
  thenCombine方法将两个无关的CompletableFuture组合起来,第二个Completable并不依赖第一个Completable的结果提供了三个方法:方法一publicU,VCompletableFutureVthenCombine(CompletionStagelt;?extendsUother,BiFunctionlt;?superT,?superU,?extendsVfn){returnbiApplyStage(null,other,fn);}方法二publicU,VCompletableFutureVthenCombineAsync(CompletionStagelt;?extendsUother,BiFunctionlt;?superT,?superU,?extendsVfn){returnbiApplyStage(asyncPool,other,fn);}方法三publicU,VCompletableFutureVthenCombineAsync(CompletionStagelt;?extendsUother,BiFunctionlt;?superT,?superU,?extendsVfn,Executorexecutor){returnbiApplyStage(screenExecutor(executor),other,fn);}
  说明:CompletionStagelt;?extendsUother参数新的CompletableFuture的计算结果BiFunctionlt;?superT,?superU,?extendsVfn参数定义了两个CompletableFuture对象完成计算后如何合并结果,该参数是一个函数式接口,因此可以使用Lambda表达式Executorexecutor参数自定义线程池同理以async结尾的方法将会在一个新的线程中执行组合操作
  示例:publicclassThenCombineTest{privatestaticRandomrandomnewRandom();publicstaticvoidmain(String〔〕args)throwsExecutionException,InterruptedException{CompletableFutureIntegerresultCompletableFuture。supplyAsync(ThenCombineTest::randomInteger)。thenCombine(CompletableFuture。supplyAsync(ThenCombineTest::randomInteger),(i,j)ij);System。out。println(result。get());}publicstaticIntegerrandomInteger(){returnrandom。nextInt(100);}}
  将两个线程计算出来的值做一个乘法在返回执行流程图:
  allOfanyOf组合多个CompletableFuture:
  方法介绍:allOfpublicstaticCompletableFutureVoidallOf(CompletableFuturelt;?。。。cfs){returnandTree(cfs,0,cfs。length1);}anyOfpublicstaticCompletableFutureObjectanyOf(CompletableFuturelt;?。。。cfs){returnorTree(cfs,0,cfs。length1);}
  说明:allOf所有的CompletableFuture都执行完后执行计算。anyOf任意一个CompletableFuture执行完后就会执行计算
  示例:allOf方法测试publicclassAllOfTest{publicstaticvoidmain(String〔〕args)throwsExecutionException,InterruptedException{CompletableFutureVoidfuture1CompletableFuture。supplyAsync((){System。out。println(hello);returnnull;});CompletableFutureVoidfuture2CompletableFuture。supplyAsync((){System。out。println(world);returnnull;});CompletableFutureVoidresultCompletableFuture。allOf(future1,future2);System。out。println(result。get());}}
  allOf方法没有返回值,适合没有返回值并且需要前面所有任务执行完毕才能执行后续任务的应用场景anyOf方法测试publicclassAnyOfTest{privatestaticRandomrandomnewRandom();publicstaticvoidmain(String〔〕args)throwsExecutionException,InterruptedException{CompletableFutureStringfuture1CompletableFuture。supplyAsync((){randomSleep();System。out。println(hello);returnhello;});CompletableFutureStringfuture2CompletableFuture。supplyAsync((){randomSleep();System。out。println(world);returnworld;});CompletableFutureObjectresultCompletableFuture。anyOf(future1,future2);System。out。println(result。get());}privatestaticvoidrandomSleep(){try{Thread。sleep(random。nextInt(10));}catch(InterruptedExceptione){e。printStackTrace();}}}
  两个线程都会将结果打印出来,但是get方法只会返回最先完成任务的结果。该方法比较适合只要有一个返回值就可以继续执行其他任务的应用场景注意点
  很多方法都提供了异步实现【带async后缀】,但是需小心谨慎使用这些异步方法,因为异步意味着存在上下文切换,可能性能不一定比同步好。如果需要使用异步的方法,先做测试,用测试数据说话!!!CompletableFuture的应用场景
  存在IO密集型的任务可以选择CompletableFuture,IO部分交由另外一个线程去执行。Logback、Log4j2异步日志记录的实现原理就是新起了一个线程去执行IO操作,这部分可以以CompletableFuture。runAsync((){ioOperation();})的方式去调用。如果是CPU密集型就不推荐使用了推荐使用并行流优化空间
  supplyAsync执行任务底层实现:publicstaticUCompletableFutureUsupplyAsync(SupplierUsupplier){returnasyncSupplyStage(asyncPool,supplier);}staticUCompletableFutureUasyncSupplyStage(Executore,SupplierUf){if(fnull)thrownewNullPointerException();CompletableFutureUdnewCompletableFutureU();e。execute(newAsyncSupplyU(d,f));returnd;}
  底层调用的是线程池去执行任务,而CompletableFuture中默认线程池为ForkJoinPoolprivatestaticfinalExecutorasyncPooluseCommonPool?ForkJoinPool。commonPool():newThreadPerTaskExecutor();
  ForkJoinPool线程池的大小取决于CPU的核数。CPU密集型任务线程池大小配置为CPU核心数就可以了,但是IO密集型,线程池的大小由CPU数量CPU利用率(1线程等待时间线程CPU时间)确定。而CompletableFuture的应用场景就是IO密集型任务,因此默认的ForkJoinPool一般无法达到最佳性能,我们需自己根据业务创建线程池

中国男篮最新17人名单,辽宁0人入选,郭艾伦缺席,周鹏回归北京时间10月29日,中国篮协正式公布最新一期男篮集训名单,周琦、赵睿和王哲林领衔17人名单,郭艾伦和赵继伟缺席,辽宁没有任何球员入选,周鹏和阿不都沙拉木回归。按照赛程安排,第……旅游到山东烟台,这15个旅游景点值得推荐养马岛:山清水秀的秘境,被称为东方夏威夷养马岛又称象岛,因相传秦始皇东巡时曾在此养马而得名,其位于牟平区城区以北9公里处,是牟平港的北部屏障,也是牟平区的渔业水平区。其以……2比0,中国新一哥诞生!吴易昺横扫夺挑战赛第三冠,创2大里程2比0横扫美国球员希尔顿,吴易昺不仅夺得罗马挑战赛冠军,而且还创造个人2大里程碑:成为中国大陆夺得挑战赛冠军最多(3个)的男球员,并超越张之臻成为中国大陆排名最高的男球员。……肿瘤科医生不容易得癌的人,通常有5个特质,全有的该偷笑30岁的小林因为腹痛到医院就诊,结果确诊了肝癌,通过询问,医生了解到,小林不仅有乙肝,而且有长期酗酒的习惯。确诊肝癌后,大家既为小林感到惋惜,又怪他太不注意了,明明有乙肝……火箭胜独行侠!波特杀死比赛,申京史密斯亮眼,替补挖三核北京时间11月17号火箭和独行侠的比赛,这场比赛火箭在大部分时间都保持领先,这是让人意外的情况。甚至在末节,火箭一直都可以稳住优势,就此10192险胜了独行侠。而格林和小……把握聪明糊涂心中要有底线小时候父母去世早,很多道理真的不懂,经常有邻居长辈提醒应该这样、那样,就这样还经常由于表现不好,被远处回来的老大揍。有人说你是在骂声中成长,在挨揍中进步。老大是我人生的领……极狐阿尔法s全新HI版,深圳城区NCA体验说起极狐汽车很多人的第一感觉应该是新势力造车其中一员,其实极狐汽车是北汽集团旗下的一个新能源品牌,其中最大的亮点是跟华为合作开发智能驾驶系统,近日极狐阿尔法S全新HI版会给深圳……S12主题曲CG公开,拳头被骂惨了!歌词太离谱被吐槽,动画无各位LPL的观众和英雄联盟召唤师大家好,这里是天下游戏汇。S12全球总决赛是在昨天晚上正式公布了主题曲MV,这次的主题曲是由黑人歌手LilNasX演唱的《StarWalk……篮网3消息!杜兰特开心回归,西蒙斯打游戏,18人大名单出炉NBA新赛季临近,篮网这边也公布了训练营时间,将在9月28日开启,27日则是媒体日,到时候总经理肖恩马克斯、主帅纳什和球员将接受采访。休赛期篮网成为焦点话题,很多记者都在等待着……在意大利维杰瓦诺大公广场感受文艺复兴荣光国际在线报道(记者冀媛):维杰瓦诺是意大利伦巴第大区帕维亚省的一个小镇,这里有文艺复兴时期意大利乃至全欧洲第一个以古罗马广场为灵感和标准修建的广场。在古罗马的概念里,广场……推荐收藏1000个新材料产业链结构图新能源动力电池、光伏、半导体、显示稀土、先进高分子、前沿新材料这些行业的产业链结构是怎么样的呢?一分钟告诉你!新能源动力电池锂电池隔膜产业链结构图电池管……为何三木妈妈杨丽萍从不摘帽子?看了她们光额形象,我悟了不会打扮那几年,常常模仿跟自己身材相似的时尚博主的穿搭,一整套复刻,却总觉得好像少了点意思。后来逐渐发现,博主们的搭配,核心不在衣服,一些看似随便戴的配饰,才是时髦感的来源。……
英超Big6引援汇总!曼城阿森纳立竿见影,切尔西曼联成了冤大距离夏季转会窗口关闭不不到48小时了,有的球队早早结束了引援,有的球队还在做最后的努力,一起盘点英超Big6这个夏窗的引援操作,看看谁最成功,谁最失意!(租借球员不在统计之内)……大疆最新款无人机AVATA优缺点评测详解概要:大疆发布了消费者期待已久的新款无人机AVATA,目前行业对此无人机赞不绝口:它是一款可以忘记操控无需学习的沉浸式无人机。实际体验也确实如此,本文将从爱好者的角度对A……兔子杯恭喜阿豪再添一冠!兔子杯金铲铲争霸赛圆满杀青恭喜阿豪再添一冠荣获兔子杯金铲铲争霸赛第一届冠军通过本次大赛兔子也见到了不少粉丝对金铲铲的热爱与执着本次大赛获奖的前8名全都在这里了除……这届世界杯冠军有多难猜?这家华尔街巨头都不敢预测!股市的世界每经编辑:黄胜据央视新闻20日消息,北京时间11月20日23点,首次在北半球冬季举行的2022年卡塔尔世界杯将正式拉开帷幕。本届世界杯哪支球队会夺冠?打脸王高盛不敢……深圳10个异域风情地,你去过几个?各种原因,没能出国游玩?其实深圳也有着不输国外的风景深圳10个充满异域风情的好去处每一个地方错过都很可惜官湖村日本小镰仓风情官湖村,又被称为……人性是永恒的话题能否发表一句人生感悟如果你了解了人性,你就看清了这个世界。如果你掌握了人性,你就不易被居心叵测的人左右,被动成为被洗脑的工具。人性有善的一面,人性亦有恶的一面,人性……美国天后蕾哈娜,最火爆的演出照蕾哈娜(Rihanna),全名罗比恩蕾哈娜芬缇(RobynRihannaFenty),1988年2月20日出生于巴巴多斯圣迈克尔区,在美国发展的巴巴多斯籍女歌手、演员、模特。……海信打擦边球的背后在刚结束的世界杯上被争议缠身的海信,最近终于有点好消息了。12月26日,在2022(第十八届)中国音视频产业大会上,海信有三款电视获得2022年度科技创新奖产品奖,不过相……卫衣渣女靴!巨美巨显腿细,绝了姐妹们双11快来了,姐妹们都准备买点什么呀给姐妹们一个建议囤货不要囤太多哦,要注意看一下日期防止过期,另外就是双11新衣服必须是要安排上的了呀,对吧哪有女孩子嫌弃自己衣服……奇葩Bug频出,苹果AirPodsPro2提醒用户换电池根据各种网上反馈,AirPodsPro2向用户发出错误提醒,建议用户尽快更换电池。当AirPodsPro2或MagSafe充电盒的电量不足时,似乎会有Bug触发附近设备上的Fi……油价调整消息今天10月2日,国内929598号汽油调整后售价距离年内第19次油价调整还剩下三个工作日,可以说是已经开始倒计时,和下半年大多数的油价调整一样,油价正面临再次下调,原因是目前调价金额的跌幅达到120元吨,同时周五原油价格下跌……下赛季有望东山再起的八支球队,凯尔特人卷土重来,公牛一骑绝尘随着杜兰特主动申请离队,自由市场再度引起惊天巨变,各支球队甚至停止先前的引援计划,静待死神杜兰特最终的选择。自由市场作为增强球队硬实力的重要首要手段,一度得到球队的青睐。……
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网