CompletableFuture异步批处理
前言
在java8的环境下,CompletableFuture是非常受大家喜欢的api,其强大的异步编排能力是做应用服务的绝佳助手。这个api里面包含了大几十个方法,咱们不挨着展开,本篇默认的都是对这个api有一些基础的小伙伴。CompletableFutureCompletionStage代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段一个阶段的计算执行可以是一个Function,Consumer或者Runnable。比如:stage.thenApply(x -> square(x)).thenAccept(x -> System.out.print(x)).thenRun(() -> System.out.println())一个阶段的执行可能是被单个阶段的完成触发,也可能是由多个阶段一起触发在Java8中,CompletableFuture提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合 CompletableFuture 的方法。它可能代表一个明确完成的Future,也有可能代表一个完成阶段( CompletionStage ),它支持在计算完成以后触发一些函数或执行某些动作。它实现了Future和CompletionStage接口
使用场景
现在有如下场景。前端传递一个用户对象的集合,拿到之后需要后端挨个处理集合中的对象,且每个对象的操作都非常地耗时,例如需要三秒左右,每一个处理完之后会返回一个结果,我需要汇总这些结果返回给前端。示意图大概如下:
挨个处理完每个对象之后,已经花费了10s,这还没算上此次其它的操作。假设业务上对这个接口的要求是5s,那么显然这样做就不满足要求了,那怎么办呢,也比较容易想到,每个处理对象的操作异步进行,最终把结果汇总下就行了。
思路如下:
结合CompletableFuture实现业务
新建user类:import lombok.Data; /** * @author : wuwensheng * @date : 10:47 2021/12/13 */ @Data public class User { private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } } 复制代码
springboot整合测试:import com.teligen.PhoneApplication; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ThreadPoolExecutor; import java.util.function.BiFunction; import java.util.function.Supplier; /** * 异步批处理的类,可以有相关的许多变种 * * @author : wuwensheng * @date : 10:36 2021/12/13 */ @RunWith(SpringRunner.class) @SpringBootTest(classes = PhoneApplication.class) @Slf4j public class CompletableFutureTest { @Autowired private ThreadPoolExecutor customThreadPoolExecutor; } 复制代码
customThreadPoolExecutor这个对象是我的线程池。
每个对user的处理都是一个CompletableFuture,如下:public CompletableFuture disposeUser(User user) { return CompletableFuture.supplyAsync(new Supplier() { @Override public Integer get() { log.info("Thread name:{}", Thread.currentThread().getName()); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } return user.getAge() + 10; } }, customThreadPoolExecutor).handleAsync(new BiFunction() { @Override public Integer apply(Integer param, Throwable throwable) { int result = param; if (throwable == null) { result = param * 2; } else { log.info("throwable is:{}", throwable.getMessage()); } return result; } }); } 复制代码
这里处理每一个user都让当前线程沉睡了两秒,用来模拟处理业务所花费的时间。
继续编排下:@Test public void userTest() { List users = new ArrayList<>(); users.add(new User("小明", 3)); users.add(new User("小红", 2)); users.add(new User("小芳", 18)); CompletableFuture[] completableFutures = users.stream().map(user -> { return disposeUser(user); }).toArray(CompletableFuture[]::new); // 等待所有任务执行完 CompletableFuture.allOf(completableFutures).join(); for (CompletableFuture completableFuture : completableFutures) { try { log.info("result:{}", completableFuture.get()); } catch (Exception e) { e.printStackTrace(); } } } 复制代码
跑一下看看结果:
ok了,57秒开始处理,59秒处理完毕。任务在join之后的确是并行的。这是一种什么感觉呢,所有异步线程出去办事了,有一辆车等着它们回来,最后一个人回来的时候,那便发车。
咱们再验证下。当处理小芳的时候沉睡5秒,看下结果:
这次返回耗费了5秒左右,处理得最慢的那个线程决定了最终的返回时长,这也符合咱们的预期。 大家在处理集合数据并且每一条的处理都比较耗时的话,可以考虑这个手法。
作者:119_115_104_104_201
链接:https://juejin.cn/post/7041087572461748232
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
这些相见恨晚的收纳神器,让我忍不住动手收藏针对收纳这个问题,Rainbow姐姐一直保持能各归各位的习惯,很少利用收纳神器这个物品进行收纳,有着收纳神器就是浪费空间的偏见。直到家里物品越来越多,各归各位的情况也越来越难以维持
7种常见类型的玄关,你家是哪一种呢?玄关是屋外到屋内的缓冲地带,成为房子的门面,而且从玄关装修的好坏就可以大致猜测出屋内装修情况,所以进门的第一印象尤为重要。因为户型的原因,形成了各种类型的玄关,而要让这些玄关具有过
多亏了这些软装陈设,让新家实现了整容级改变费尽周折新房终于装修完了,新一轮拷问直接心灵有点丑怎么办?本以为熬过了找设计师,等装修工人装修,让新房通风散味,就可以直接拎包入住。但没想到,失去温度的陈设,过于单调的布置,都让人
180现代风,五口之家的理想居所前言本期案例为旧房改造,原有的装潢布局已经不符合屋主一家的审美标准和生活需求,在商讨过后决定给自己的小家做一次全方位整改,于是有了这次合作。经过探讨商榷,最终以屋主一家喜爱的现代风
拯救土味精装房,只要做这几步精装房算得上是时下的大趋势,无需自己搞硬装,只需要添置喜欢的家具后即可拎包入住,想想都觉得便利。但现实充满打脸时刻,订房前觉得甚是欢喜,可能在交房时就会感受到当头棒喝,地板太丑墙壁
美观又实用,卫浴五金这样选让你避坑卫生间虽然在整个户型中占据区域较小,却是一个非常重要的部分,洗漱整理活动,基本上每天都要使用或经过这个区域。而在使用的过程中,总会因为一些因为一些五金物件,比如花洒地漏水龙头的选购
怎么挑选卫浴五金?这道题,并不难解五金配件在家装中是相对容易被忽略的物件,但实际上,这些看似不起眼的配件却对我们的整个家居设计,包括家具使用都起到至关重要的作用。五金配件涵盖的范围很广,门窗五金厨房五金装饰五金卫浴
打造符合心意的洗衣房,靠这些就够了洗衣房的设定,是家中较为特殊的一部分,它可以在空间允许的情况下单独为一个区域,也可以依附于阳台或者卫生间进行设计。主要有洗衣晾晒和储存物品等功能,所以在规划之时,除了要注意整体格局
又被好物加持过的走廊,美到了走廊,作为空间中比较容易忽视的区域,除了充当连接客厅卧室餐厅等区域的过道以外,几乎常被闲置,毫无利用率。于是,有的走廊占据较多的户型,常常会因为过于留白的设计,导致整个户型的视觉效
这些家用电器,让你重拾节后的下厨乐趣年后开工,就要开启自己动手,丰衣足食的生活了,特别是厨房这块区域,在被冷落一段时间后,也应该加点新花样,给生活增添点新的活力了。而好的厨房,除了该有的干净和整洁之外,还应该拥有一些
神仙物件集合,自己动手就能二次改造精装房精装修后的房子虽然节省了入住时间,但在美观程度上还是有些不尽人意。比如墙壁设计太单调,地板铺贴太老气,装饰摆件不时尚等问题,总让人想将房子重新翻新。而今天造居君分享的好物,不用特意