侧边栏壁纸
博主头像
小明锅博主等级

没错,我就是小明,不过已经长大了,成为一名码农,在搬砖的同时,喜欢分享Java的编程知识,本网站致力于一站式后端人员开发,解决码农日常问题,挤出更多moyu时间

  • 累计撰写 15 篇文章
  • 累计创建 7 个标签
  • 累计收到 2 条评论
标签搜索

目 录CONTENT

文章目录

并发编程--使用CompletableFuture优化查询接口

小明锅
2024-04-15 / 0 评论 / 0 点赞 / 665 阅读 / 6,130 字 / 正在检测是否收录...

一、需求背景

相信大家入职到一个新公司(xinkeng),一般都会被分配去解决千古难题(caipigu),这些问题大概率很多不想去解决,并且代码很多复杂,各种套娃。领导为检验新人的能力,就会去让新人去解决这种问题,一方面可以了解业务代码,还能顺手把没人愿意做的事做了。但是,你做不好也没关系,大佬也会说,这个其实都是历史问题了,大家是不是有遇到这样奇葩新手工作呢?欢迎大家评论区讨论。今天给大家分享一个比较常见的问题,接口超时或者响应时间比较长怎么解决。

本文主要介绍,如何同个CompletableFuture异步编排,来解决分布多次查询,导致接口响应慢的问题。

二、CompletableFuture介绍与实战

1.为什么要用CompletableFuture呢?

大家都知道,多线程可以解决,多次查询第三方接口或者数据库耗时的问题?但是有种情况,可能就很难去解决了,比如一个异步调用可能会依赖另一个异步调用的执行结果。因为多线程一整个是一个任务,多线程可能就不好处理。所以Java8就推出了一个新的并发框架—CompletableFuture。可以参考书籍:《实战java高并发程序设计》 、《Java8实战》

2.CompletableFuture常用接口方法

1)CompletableFuture启动异步任务

CompletableFuture提供四种静态方法来创建一个异步操作,可以自定义线程池,否没有传Executor则会使用默认线程池。

无返回值的:
    public static CompletableFuture<Void> runAsync(Runnable runnable)
    public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)
有返回值的:
    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

2)CompletableFuture完成回调以及异常感知

监听正常和异常结果

whenCompleteAsync((res,excption):第一个参数是返回结果,第二参数是异常

whenComplete: 是执行当前任务的线程用来继续执行whenComplete的任务

whenCompleteAsync: 是执行whenCompleteAsync的任务继续提交个线程池来进行执行

处理异常结果

exceptionally

 CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getName());
            int i = 10 / 0;
            System.out.println("运行结果:" + i);
            return i;
        }, threadPool)
        .whenComplete((res,excption)->{
            System.out.println("结果是:"+res + ",异常是:"+excption);
        }).exceptionally(throwable ->{
            // ruguo 异常就会返回改结果
            return 10;
        });

3)CompletableFuture最终处理结果

CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getName());
            int i = 10 / 0;
            System.out.println("运行结果:" + i);
            return i;
        }, threadPool)
            .handle((res,excption)->{
            if(res != null){
                return res * 2;
            }
            if(excption != null){
                return 0;
            }
            return 0;
        });

4)CompletableFuture线程串行化

thenRunAsync:没有返回值值,不能接受上一步的结果

thenAcceptAsync:没有返回值值,能接受上一步的结果

thenApplyAsync:有返回值值,能接受上一步的结果

/*
        thenRunAsync:没有返回值值,不能接受上一步的结果
        * */
        CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getName());
            int i = 10 / 5;
            System.out.println("运行结果:" + i);
            return i;
        }, threadPool).thenRunAsync(()->{
            System.out.println("任务2启动。。。");
        },threadPool);

          /*
        thenAcceptAsync:没有返回值值,能接受上一步的结果
        * */
        CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getName());
            int i = 10 / 5;
            System.out.println("运行结果:" + i);
            return i;
        }, threadPool).thenAcceptAsync(res->{
            System.out.println("任务2启动。。。"+res);
        },threadPool);


        /*
        thenApplyAsync:有返回值值,能接受上一步的结果
        * */
       CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getName());
            int i = 10 / 5;
            System.out.println("运行结果:" + i);
            return i;
        }, threadPool).thenApplyAsync(res->{
           System.out.println("任务2启动。。。"+res);
            return res +1;
       });

5)CompletableFuture多任务任务组合

CompletableFuture.allOf:主线程所有任务完成执行,用于解决多个任务单独get阻塞问题

CompletableFuture.anyOf:主线程任意1个任务完成执行

        CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的图片信息");
            return "hello.img";
        }, threadPool);
        CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的属性");
            return "黑色+256G";
        }, threadPool);
        CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {

            try {
                Thread.sleep(3000);
                System.out.println("查询商品介绍");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "华为";
        }, threadPool);
        // 顺序获取,同步执行,效率低
/*      futureImg.get();
        futureAttr.get();
        futureDesc.get();*/
        CompletableFuture<Void> allOf = CompletableFuture.allOf(futureImg, futureAttr, futureAttr);
        allOf.get();
        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureAttr);
        anyOf.get();
        

3.CompletableFuture工作实战案例

这是本人在电商公司中,解决一个问题,对于电商一个商品(sku)包含的信息需要查找很多表或者接口,如果全都用同步来处理肯定是比较慢的,如果用多线程,对于依赖调用又不好处理。像sku基本信息、sku的图片信息互不依赖可以异步执行,但是获取商品的销售属性组合、获取商品的介绍、获取商品的规格参数信息都要依赖sku基本信息执行结果,才能进一步去查询。所以,显然CompletableFuture首选。不说废话,直接上关键代码案例:

        //1.sku基本信息获取
        CompletableFuture<SkuInfoEntity> skuInfoFuture = CompletableFuture.supplyAsync(() -> {
            SkuInfoEntity info = getById(skuId);
            skuItemVo.setInfo(info);
            System.out.println("skuInfoFuture...1");
            return info;
        }, threadExecutor);
        
        //2.获取spu的销售属性组合
        CompletableFuture<Void> saleAttrFuture = skuInfoFuture.thenAcceptAsync((res) -> {
            List<SkuItemSaleAttrVo> saleAttr = skuSaleAttrValueService.getSaleAttrsBySpuId(res.getSpuId());
            skuItemVo.setSaleAttr(saleAttr);
            System.out.println("saleAttrFuture...2");
        }, threadExecutor);

        //3.获取spu的介绍
        CompletableFuture<Void> descFuture = skuInfoFuture.thenAcceptAsync((res) -> {
            SpuInfoDescEntity spuInfoDesc = spuInfoDescService.getById(res.getSpuId());
            skuItemVo.setDesc(spuInfoDesc);
            System.out.println("descFuture...3");
        }, threadExecutor);

        //4.获取spu的规格参数信息
        CompletableFuture<Void> attrGroupFuture = skuInfoFuture.thenAcceptAsync((res) -> {
            List<SpuItemAttrGroupVo> groupAttrs = attrGroupService.getAttrGroupBySpuId(res.getSpuId(), res.getCatalogId());
            skuItemVo.setGroupAttrs(groupAttrs);
            System.out.println("attrGroupFuture...4");
        }, threadExecutor);

        //5.sku的图片信
        CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
            List<SkuImagesEntity> images = imagesService.getImagesBySkuId(skuId);
            skuItemVo.setImages(images);
            System.out.println("imageFuture...5");
        }, threadExecutor);

        //6.查询当前sku是否参与秒杀优惠
        CompletableFuture<Void> seckillSkuInfoFuture = CompletableFuture.runAsync(() -> {
            Result<SeckillSkuRedisVO> seckillSkuInfo = seckillFeignService.getSeckillSkuInfo(skuId);
            if (seckillSkuInfo.getCode() == 200) {
                SeckillSkuRedisVO skuInfoData = seckillSkuInfo.getData();
                skuItemVo.setSeckillSkuVo(skuInfoData);

            }
        }, threadExecutor);


        CompletableFuture.allOf(skuInfoFuture, saleAttrFuture, descFuture, attrGroupFuture, imageFuture, seckillSkuInfoFuture).get();

三、CompletableFuture总结

CompletableFuture真的很适合做异步任务,或者接口多次调用第三服务等复杂操作,可以极大的提高程序响应速度。重要的是,可以提升java开发能力,对于日常工作,在整天写curd的过程中,反复思考有没有更好的方案,比如多线程,异步编排等。而不是老是写流水线代码。

0

评论区