zoukankan      html  css  js  c++  java
  • java8的parallelStream提升数倍查询效率

    业务场景

    在很多项目中,都有类似数据汇总的业务场景,查询今日注册会员数,在线会员数,订单总金额,支出总金额等。。。这些业务通常都不是存在同一张表中,我们需要依次查询出来然后封装成所需要的对象返回给前端。那么在此过程中,就可以把这个接口中“大任务”拆分成N个小任务,异步执行这些小任务,等到最后一个小任务执行完,把所有任务的执行结果封装到返回结果中,统一返回到前端展示。

    同步执行

    首先看看同步执行的代码

    public class Test {
    
    
        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        @ToString
        class Result {
            /**
             * 在线人数
             */
            Integer onlineUser;
    
            /**
             * 注册人数
             */
            Integer registered;
    
            /**
             * 订单总额
             */
            BigDecimal orderAmount;
    
            /**
             * 支出总额
             */
            BigDecimal outlayAmount;
        }
    
        @org.junit.Test
        public void collect() {
            System.out.println("数据汇总开始");
            long startTime = System.currentTimeMillis();
            Integer onlineUser = queryOnlineUser();
            Integer registered = queryRegistered();
            BigDecimal orderAmount = queryOrderAmount();
            BigDecimal outlayAmount = queryOutlayAmount();
            Result result = new Result(onlineUser, registered, orderAmount, outlayAmount);
            long endTime = System.currentTimeMillis();
            System.out.println("获取汇总数据结束,result = " + result);
            System.out.println("总耗时 = " + (endTime - startTime) + "毫秒");
        }
    
        public Integer queryOnlineUser() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("查询在线人数 耗时2秒");
            return 10;
        }
    
        public Integer queryRegistered() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("查询注册人数 耗时2秒");
            return 10086;
        }
    
        public BigDecimal queryOrderAmount() {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("查询订单总额 耗时3秒");
            return BigDecimal.valueOf(2000);
        }
    
        public BigDecimal queryOutlayAmount() {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("查询支出总额 耗时3秒");
            return BigDecimal.valueOf(1000);
        }
    
    }

    执行时长想必大家都能够想得到,理所应当是10秒以上

    数据汇总开始
    查询在线人数 耗时2秒
    查询注册人数 耗时2秒
    查询订单总额 耗时3秒
    查询支出总额 耗时3秒
    获取汇总数据结束,result = Test.Result(onlineUser=10, registered=10086, orderAmount=2000, outlayAmount=1000)
    总耗时 = 10008毫秒

    异步执行

    下面换成异步执行,用java8的parallelStream(并行流),这里为什么不用Thread呢,这里有一个注意点,我们需要获取所有所有子任务执行完的时间点,在这个时间点之后才能将结果封装返回,Thread没有办法满足,这里parallelStream和函数式接口就登场了。

    java8的特性之一 —— lambda表达式,就是配合函数式接口使用的。

    java8内置了四大核心函数式接口:

     1、Consumer<T>   : 消费型接口    void accept(T t);

     2、Supplier<T>      : 供给型接口    T get();

     3、Function<T,R>   : 函数型接口    R apply(T t);

     4、Predicate<T>    : 断言型接口    boolean test(T t);

    这四大核心函数式接口其下还有很多子接口,基本上能满足日常项目所用,这里扯远了。。   直接上代码。

    这里我们需要使用的是Runable接口,是无参无返回值的一个接口。在实际场景中,可能有时间范围之类的查询参数的,则可以根据不同业务使用不同的接口。这种方式也可以用Future接口去实现,有兴趣的可以试一试,这里就不多做叙述了。

    @org.junit.Test
    public void collect() {
        System.out.println("数据汇总开始");
        long startTime = System.currentTimeMillis();
        Result result = new Result();
        List<Runnable> taskList = new ArrayList<Runnable>() {
            {
                add(() -> result.setOnlineUser(queryOnlineUser()));
                add(() -> result.setRegistered(queryRegistered()));
                add(() -> result.setOrderAmount(queryOrderAmount()));
                add(() -> result.setOutlayAmount(queryOutlayAmount()));
            }
        };
        taskList.parallelStream().forEach(v -> v.run());
        long endTime = System.currentTimeMillis();
        System.out.println("获取汇总数据结束,result = " + result);
        System.out.println("总耗时 = " + (endTime - startTime) + "毫秒");
    }

    执行结果,由于四个子任务都是并行的,效率直接提升了三倍,如果子任务越多的话提升效果越明显。

    数据汇总开始
    查询在线人数 耗时2秒
    查询注册人数 耗时2秒
    查询订单总额 耗时3秒
    查询支出总额 耗时3秒
    获取汇总数据结束,result = Test.Result(onlineUser=10, registered=10086, orderAmount=2000, outlayAmount=1000)
    总耗时 = 3079毫秒

    总结

    1.parallelStream是异步编程的好帮手,在使用过程中一定要注意线程安全的问题。

    2.以上这种方式只能用在没有事务的业务中,因为在多线程中,事务是不共享的。

  • 相关阅读:
    购物车程序
    python学习第二节 数据类型、字符编码、文件处理
    while实现2-3+4-5+6...+100 的和
    给文件加权限
    查询数据插入新表格
    归档程序错误。在释放之前仅限于内部连接
    查看Linux环境变量
    查找文件命令
    ORACLE导入导出操作篇
    oracle中使用minus进行数据排除(类似SqlServer except函数)
  • 原文地址:https://www.cnblogs.com/-tang/p/13283216.html
Copyright © 2011-2022 走看看