当前位置: 首页 > 科技观察

说说JDK8的CompletableFuture,你懂吗?

时间:2023-03-22 01:51:38 科技观察

前段时间,阿芬曾经提过CompletableFuture,但还是有读者表示感觉不清楚,有点乱。今天阿粉就说说这个CompletableFuture的一些API方法。CompletableFutureCompletableFuture是java8中java.util.concurrent库新增的主要工具,与传统的Future相比,它支持流式计算、函数式编程、完成通知、自定义异常处理等诸多新特性。supplyAsync方法通过该函数创建的CompletableFuture实例将异步执行当前传入的计算任务。在调用端,可以通过get或者join获取最终的计算结果。这个有两种不同的实现方式,一种是传入我们创建的线程池,然后使用我们创建的线程池进行操作,另一种是不传入线程池,让程序使用默认线程池操作。//使用默认线程池publicstaticCompletableFuturesupplyAsync(Suppliersupplier){returnasyncSupplyStage(asyncPool,supplier);}//使用自定义线程池publicstaticCompletableFuturesupplyAsync(Suppliersupplier,Executorexecutor){returnasyncSupplyStage(screenExecutor(executor),supplier);第一种只需要传入一个Supplier实例(通常使用lamda表达式),框架会默认使用ForkJoin线程Pool来执行提交的任务。我们定义一段代码看看:ExecutorServiceexecutors=Executors.newFixedThreadPool(5);CompletableFuture>bFuture=CompletableFuture.supplyAsync(()->{//执行查询QueryWrapperqueryWrapper=newQueryWrapper<>();queryWrapper.eq("userId",params.getUserId());queryWrapper.eq("SHOP_ID",params.getShopId());Listlist=orderService.list(queryWrapper);返回列表;},executors);当我们执行查询时,此时实际上是一个异步查询。我们可以编写多个查询。比如我们在上面的代码中查询订单。接下来,我们可以查询用户的信息,或者使用同一个线程池。CompletableFuture>aFuture=CompletableFuture.supplyAsync(()->{//执行查询QueryWrapperqueryWrapper=newQueryWrapper<>();queryWrapper.eq("SHOP_ID",params.getShopId());Listlist=userService.list(queryWrapper);返回列表;},executors);这时候我们就可以拿到返回的结果了,那么需要调用什么方法才能拿到返回的结果呢?可以通过get或者join获取最终的计算结果。这时候我们可以打印出来得到结果的长度。列表<用户>list=aFuture.get();列表list=bFuture.get();这样我们就可以使用异步查询来完成我们对结果集的查询了。在这里,有朋友想问,如果我想用第一个的结果在第二个数据中查询,怎么办?thenApplythenApply提交的任务类型必须符合Function签名,即有入参和返回值,其中入参为前置任务的结果。这意味着什么?其实很简单,直接看这里的代码:CompletableFuture>cFuture=bFuture.thenApply((list)->{Listcollect=list.stream().map(User::getUserId).collect(Collectors.toList());...返回orderList;});其实我们直接根据查询到的所有用户的集合获取他的userId,然后我们把这些用户下的订单根据UserId全部提取出来。当然,在实际使用中,我们理论上可以无限接续后续计算任务,实现链条更长的流计算。但是这种链接不是很容易使用。毕竟线程池是要控制的。大家记住,使用完成后,可以回调自己创建的线程池,调用shutdown方法。我们继续。thenAccept的效果其实和thenApply是一样的,但是thenAccept提交的任务类型必须符合Consumer签名,即有入参无返回值,入参为前驱任务的结果.其实调用和之前一样,只是没有返回值。CompletableFuturecFuture=bFuture.thenAccept((list)->{Listcollect=list.stream().map(User::getUserId).collect(Collectors.toList());...});这就是区别,不需要return,这种通用的使用比较特殊,比如我们执行了很多操作,但是我们需要混合其中的一些,比如通知某个服务,但是我们不需要这个服务返回值的结果给我们的时候,这个方法其实是可以用的。方便快捷。当然,如果要直接打电话,阿芬也没有异议。whenCompletewhenComplete主要用于注入任务完成时的回调通知逻辑。这样就解决了传统future在任务完成时无法主动发起通知的问题。前置任务会将计算结果或抛出的异常作为入参传递给回调通知函数。这个方法相当于把上一个任务的结果拿过来通过第二种方法获取结果,不会影响第二种的逻辑。示例代码如下:CompletableFuture>listCompletableFuture=bFuture.whenComplete((r,e)->{if(e!=null){System.out.println("computefailed!");}else{System.out.println("接收到的结果是"+r);}});事实上,最后承诺的是收件人的信息。你学会了吗?