【惊人揭秘】Java并行流魔法:600ms查询秒变260ms,效率飙升120%的极速之旅!

我在前几天写需求的时候遇到了这样的一个问题,就是通过设备Id反查设备监测类型,并返回24小时的数据情况;

但是这里设计到多次查询,导致一个简单的查询竟然需要消耗600ms,忍不了,太慢了,必须跟我一样做风一样的男人。

于是乎我开始着手修改此事,以下是源代码:

//检查时间
        final String currentDate = StringUtils.isEmpty(date) ? LocalDate.now().toString() : date;
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        //不用做验证,因为他们前端直接点的设备 根据id查询设备,获取他都监测类型,并根据,分割,使用并行流加快速度
        String deviceMonitorType = deviceInfoMapper.selectOne(DeviceInfoDO::getId, equipmentId).getDeviceMonitorType();
        if (StringUtils.isEmpty(deviceMonitorType)) return null;
        return Arrays.stream(deviceMonitorType.split(","))
                .collect(Collectors.toMap(Function.identity(), type -> equipmentDataMapper.getEquipmentDataById(equipmentId, currentDate, type,userId)));

这个查询竟然要执行600ms左右,于是我想到了上篇文章中提到的“CompletableFuture”,来执行他,结果并不理想,进入还需要550ms左右,我在 想还有什么优化方案吗?

我们先分析:

1.肯定是多次查询

2.不能修改为单次查询(应该可以,但是sql修改量太大了,没必要)

忽然灵光乍现,想起来并行流,于是忽,我开始着手尝试;

以下是代码

 public Map<String, List<Map<String, Object>>> getEquipmentDataById(String equipmentId, String date) {
        //检查时间
        final String currentDate = StringUtils.isEmpty(date) ? LocalDate.now().toString() : date;
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        //不用做验证,因为他们前端直接点的设备 根据id查询设备,获取他都监测类型,并根据,分割,使用并行流加快速度
        String deviceMonitorType = deviceInfoMapper.selectOne(DeviceInfoDO::getId, equipmentId).getDeviceMonitorType();
        if (StringUtils.isEmpty(deviceMonitorType)) return null;
        return Arrays.stream(deviceMonitorType.split(","))
                .parallel()
                .collect(Collectors.toMap(Function.identity(), type -> equipmentDataMapper.getEquipmentDataById(equipmentId, currentDate, type,userId)));
    }

我就添加了一个小小的并行方法,发现速度已经提升到260ms左右,于是我欣喜,我们目标是降低至300ms左右,之前我一直 不理解并行流的使用时刻,这不就是一个很好的例子,时间量多,而且要多次查询,用并行流真的是绝秒之举;

Java Stream 并行流介绍
Java Stream API 是 Java 8 引入的一个重要特性,它提供了一种高效、灵活的方式来处理集合(Collection)中的数据。Stream API 支持两种主要的数据流处理方式:顺序流(Sequential Stream)和并行流(Parallel Stream)。本文将重点介绍 Java Stream 并行流。

一、并行流的定义
并行流是 Java 8 引入的一种并行计算模型,它允许对集合类型数据进行高效的并行处理。底层实现使用了多线程技术,利用多个处理线程同时对数据进行处理,以加快计算速度。在使用并行流时,数据流会被分成多个独立的子流,并行执行处理操作。每个子流都会被分配到不同的线程上同时进行处理,最终处理结果会被合并为一个整体结果。

二、并行流的实现机制
并行流的实现基于 Fork/Join 框架,这是 Java 中用于处理递归型分治任务的标准框架。Fork/Join 框架使用了一种递归的分治策略,将任务划分为多个小任务,然后并行执行这些小任务,并最终将结果合并起来。在并行流的实现中,数据会被分割成多个数据块,每个数据块会被分配给一个线程进行处理。每个线程只负责处理自己分配到的数据块,避免了线程之间的竞争和同步操作。

三、并行流的使用方法
在 Java 中,可以通过调用集合的 .parallelStream() 方法或顺序流的 .parallel() 方法来创建并行流。例如:

java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);  
// 使用 parallelStream() 方法创建并行流  
Stream<Integer> parallelStream = numbers.parallelStream();  
  
// 或者,先创建顺序流,再转换为并行流  
Stream<Integer> anotherParallelStream = numbers.stream().parallel();
这两种方法在功能和性能上是相同的,它们都可以将集合转换为并行流,并在并行处理数据时,使用相同的底层实现机制。

四、并行流的适用场景
并行流在处理大规模数据集或执行复杂计算操作时能够显著提高程序的执行效率。具体来说,以下场景适合使用并行流:

大规模数据集:当需要处理的数据量非常大时,使用并行流可以充分利用多核处理器的优势,将数据切分成多个小块,并在多个线程上并行处理,从而缩短处理时间。
复杂计算操作:对于复杂的计算操作,使用并行流可以加速计算过程。由于并行流能够将计算操作分配到多个线程上并行执行,因此可以有效地利用多核处理器的计算能力,提高计算的速度。
无状态转换操作:并行流在执行无状态转换操作(如 map、filter)时表现较好。这类操作不依赖于其他元素的状态,每个元素的处理是相互独立的,可以很容易地进行并行处理。
五、并行流的注意事项
线程安全问题:并行流的操作是在多个线程上并行执行的,因此需要注意线程安全问题。如果多个线程同时访问共享的可变状态,可能会导致数据竞争和不确定的结果。在处理并行流时,应避免共享可变状态,或者采用适当的同步措施来确保线程安全。
性能评估:并行流的性能提升并不总是明显的。在选择使用并行流时,应根据具体情况进行评估和测试,以确保获得最佳的性能提升。有时,并行流的开销(如线程的创建和销毁、数据切割和合并等)可能超过了其带来的性能提升。
操作的可并行性:并行流适用于那些可以被独立处理的操作。如果操作之间存在依赖关系或者需要共享状态,那么并行化可能会引入线程同步的开销,降低性能。
综上所述,Java Stream 并行流是一种强大的并行计算工具,它允许开发人员以声明性的方式处理集合数据,并通过多线程技术提高程序的执行效率。然而,在使用并行流时,也需要注意线程安全问题、性能评估以及操作的可并行性等因素。

以上是并行流的介绍,有兴趣的可以看一下,大概的意思就是说,他类似于多线程,可能会有安全隐患问题,而且在数据少的情况下,并不适用,所有说,任何东西的使用都要看自己的情况,例如:我上一个遇到的问题就适合用异步来解决,这个就更适合使用并行流来解决;

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值