大聪明教你学Java | parallelStream().forEach() 的踩坑日记

news2025/1/14 0:44:35

前言

🍊作者简介: 不肯过江东丶,一个来自二线城市的程序员,致力于用“猥琐”办法解决繁琐问题,让复杂的问题变得通俗易懂。
🍊支持作者: 点赞👍、关注💖、留言💌~

最近大聪明一直在开发项目中的新需求,其中有一个需求是“解析文件(.txt文件,一行就是一条数据)中的数据并进行入库操作”。其实这个需求也很简单,无非就是将文件中每一行数据转换为一个对象,将每一个对象都存储到 list 集合中,最终执行批量入库的操作。但就是这么一个简单的需求却让我踩了一个大坑…

踩坑日记

在这里插入图片描述
各位小伙伴先看一下上图中的代码,不知道各位小伙伴有没有看出什么问题呢?👆 可能这么看起来有些不好理解,咱们再简化一下图中的代码,如下所示:👇

public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            list.add(i);
        }
        System.out.println("a:"+list.size());
        List<Integer> streamList = new ArrayList<>();
        list.parallelStream().forEach(streamList::add);
        System.out.println("b:"+streamList.size());
    }
}

各位小伙伴看看简化后的代码,猜测一下 a 和 b 输出的值分别是多少呢?这里就不卖关子了,咱们直接揭晓答案 👇

在这里插入图片描述
结果可能和大多数小伙伴猜测的都不太一样,a 和 b 的值居然不相等,且 b 的值 永远都会小于 a,同时在多次执行之后可能会出现数组下标越界异常,显然这里的代码是不符合逻辑的😥

这也是我在项目中遇倒的问题所在,解析完文件后,通过 parallelStream().forEach() 遍历结果进行处理,但是最终入库的数据条数总是小于文件中的数据条数。

这种情况大聪明还是第一次见到,不过却又一次激起了大聪明的求知欲,开启了刨根问底模式~

刨根问底

经过大聪明的一番探索,也是终于找到了问题答案… 👇

Stream(流)是 JDK8 中引入的一种类似与迭代器(Iterator)的单向迭代访问数据的工具。ParallelStream 则是并行的流,它通过 Fork/Join 框架来拆分任务,加速流的处理过程。Fork/Join 的框架是通过把一个大任务不断 fork 成许多子任务,然后多线程执行这些子任务,最后再 Join 这些子任务得到最终结果。咱们回到实例代码中来解释一下,就是先将 list 集合 fork 成多段,然后多线程添加到 streamList 的结合中,而 streamList 是ArrayList 类型,ArrayList 的 add() 方法并不能保证原子性。

咱们先看一下 ArrayList 中 add() 方法的源码👇

在这里插入图片描述
众所周知,ArrayList 作为 Collection 中极重要的一员,是非线程安全的,所以 ArrayList 并不适合多线程高并发的情况,在多线程高并发时会出现内部某些位置为 null 的情况。核心原因是,ArrayList 的add() 的方法不是线程安全的,是非原子性的,add操作可以简单理解为两个步骤:

  • ensureCapacityInternal(size + 1) :确认当前 ArrayList 中的数组是否还可以加入新的元素。如果不行,就会再申请一个:int newCapacity = oldCapacity + (oldCapacity >> 1) 大小的数组(即容量变为原来的 1.5 倍),然后将数据复制过去。
  • elementData[size++] = e:将元素添加到 elementData 数组中。

那么在多线程高并发情况下,如果有A、B两个线程同时执行 add() 方法,在第一步校验数组容量时,A、B线程都发现当前无需扩容,还可以继续添加一个元素;因此A、B线程都进入了第二步,此时,A线程先执行完,数组容量已满,然后B线程再对 elementData 赋值时,就会出现我们上面说到的情况,要么是数据丢失,要么是抛出数组下标越界的异常。

解决方案

问题原因我们已经找到了,那么问题的解决方案也就呼之欲出了~ 😁

🍊 方案一:将 parallelStream 改成 stream,或者直接使用 foreach 遍历处理。也就是放弃多线程的写法,改为传统的单线程处理。

🍊 方案二:使用 list = new CopyOnWriteArrayList<>(); 这是个线程安全的类。从源码上看,CopyOnWriteArrayList 在 add 操作时,通过 ReentrantLock 进行加锁,防止并发写。但是每次 add 操作都是把原数组中的元素拷贝一份到新数组中,然后在新数组中添加新元素,最后再把引用指向新数组,这也就会频繁的创建数组(千万别忘了数组需要一块连续的内存空间)。所以当实际业务逻辑中存在大量 add 操作时,要谨慎使用 CopyOnWriteArrayList 。

🍊 方案三:使用包装类 list = Collections.synchronizedList(Arrays.asList());

我们在使用 parallelStream 之前,一定要仔细思考一下自己的业务逻辑是否真的需要多线程并发处理。其实在实际应用场景中,并不是所有的问题都适合使用并发来解决,比如当数据量不大时,顺序执行往往比并行执行更快,毕竟准备线程池和其它相关资源也是需要时间的。但是,当任务涉及到 I/O 操作并且任务之间不互相依赖时,那么并行化就是一个不错的选择。

小结

本人经验有限,有些地方可能讲的没有特别到位,如果您在阅读的时候想到了什么问题,欢迎在评论区留言,我们后续再一一探讨🙇‍

希望各位小伙伴动动自己可爱的小手,来一波点赞+关注 (✿◡‿◡) 让更多小伙伴看到这篇文章~ 蟹蟹呦(●’◡’●)

如果文章中有错误,欢迎大家留言指正;若您有更好、更独到的理解,欢迎您在留言区留下您的宝贵想法。

你在被打击时,记起你的珍贵,抵抗恶意;
你在迷茫时,坚信你的珍贵,抛开蜚语;
爱你所爱 行你所行 听从你心 无问东西

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/615578.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot学生管理系统(含源码+数据库)

本次系统开发所用到的Java语言、Spring框架、SpringMVC框架、MyBatis框架、SpringBoot框架以及MySQL。 1.系统分析 &#xff08;1&#xff09;教师管理需求&#xff0c;学校想轻松的查阅指定教师的信息&#xff0c;学校对教师进行一个基本的信息管理&#xff0c;学校可以方便…

【python】脚本编写

这里写自定义目录标题 欢迎使用python来编写脚本环境搭建 欢迎使用python来编写脚本 测试方向&#xff0c;测试报告&#xff0c;单元测试 环境搭建 python环境搭建 下载地址 https://www.python.org/ 文档 https://docs.python.org/3/ pycharm的环境 使用chatgpt来实现代码功…

【安全架构】

概念 安全是产品的属性&#xff0c;安全的目标是保障产品里信息资产的保密性&#xff08;Confidentiality&#xff09;、完整性&#xff08;Integrity&#xff09;和可用性&#xff08;Availability&#xff09;&#xff0c;简记为CIA。 保密性&#xff1a; 保障信息资产不被未…

通过Visual Studio诊断工具定位软件CPU瓶颈

通过VS诊断工具定位软件CPU瓶颈 前情提示&#xff1a;正常情况下我们使用调试模式会看不到诊断工具窗口&#xff0c;控制台会报“无法启动标准收集器。请尝试修复 Visual Studio 的安装。 (HRESULT: 0xe1110002)”这样的错误。 解决方式&#xff1a;通过[Downloads - Visual St…

00后是太恐怖了,工作没两年,跳槽到我们公司起薪20K都快接近我了

在程序员职场上&#xff0c;什么样的人最让人反感呢? 是技术不好的人吗?并不是。技术不好的同事&#xff0c;我们可以帮他。 是技术太强的人吗?也不是。技术很强的同事&#xff0c;可遇不可求&#xff0c;向他学习还来不及呢。 真正让人反感的&#xff0c;是技术平平&…

【JAVA】---逆波兰表达式

一. 逆波兰表达式的介绍 逆波兰表达式又称为后缀表达式&#xff0c;代表的含义是操作数在前&#xff0c;运算符在后。 比如&#xff1a;12&#xff0c;用逆波兰表达式来写的话&#xff0c;就是12。 而12这种写法称为中缀表达式&#xff0c;即运算符在两个操作数之间&#xff0c…

Office Visio 2019安装教程

哈喽&#xff0c;大家好。今天一起学习的是Visio 2019的安装&#xff0c;这是一个绘制流程图的软件&#xff0c;用有效的绘图表达信息&#xff0c;比任何文字都更加形象和直观。Office Visio 是office软件系列中负责绘制流程图和示意图的软件&#xff0c;便于IT和商务人员就复杂…

测试老鸟总结,自动化测试难点挑战应对方法,我的进阶之路...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Python自动化测试&…

Redis 高级数据结构 HyperLogLog

介绍 HyperLogLog(Hyper[ˈhaɪpə(r)])并不是一种新的数据结构(实际类型为字符串类型)&#xff0c;而是一种基数算法,通过HyperLogLog可以 利用极小的内存空间完成独立总数的统计&#xff0c;数据集可以是IP、Email、ID等。如果你负责开发维护一个大型的网站&#xff0c;有一天…

Vue.js 如何进行打包部署

Vue.js 中的打包部署 Vue.js 是一款流行的前端框架&#xff0c;它提供了一种简单、灵活的方式来构建用户界面。在开发完成后&#xff0c;需要对 Vue.js 应用程序进行打包部署&#xff0c;以便在生产环境中使用。本文将介绍 Vue.js 中的打包部署以及如何进行打包部署。 打包部署…

运维小白必学篇之基础篇第十七集:NFS和DHCP实验

NFS和DHCP实验 目录 NFS和DHCP实验 环境配置&#xff1a; 实验题1&#xff1a;实现NFS服务 实验题2&#xff1a;实现DHCP服务 实验作业&#xff1a; 计算机1配置如下&#xff1a;&#xff08;计算机名为姓名首拼&#xff0c;例hy01&#xff0c;hy02...&#xff09;基础环…

为什么初学者都先学C语言?

不少高校选择C语言&#xff0c;主要C语言是一种相对底层的语言&#xff0c;学习它可以让学习者更好的理解计算机的基本原理和编程的基础概念&#xff0c;比如变量、函数、指针等。这些基础知识对于理解其他高级语言和解决复杂的编程问题都非常重要。 另外就是C语言对算法和数据…

最小化微服务漏洞

最小化微服务漏洞 目录 本节实战 实战名称&#x1f498; 案例&#xff1a;设置容器以普通用户运行-2023.5.29(测试成功)&#x1f498; 案例&#xff1a;避免使用特权容器&#xff0c;选择使用capabilities-2023.5.30(测试成功)&#x1f498; 案例&#xff1a;只读挂载容器文件…

Redis进阶:主从复制、集群搭建

Redis进阶 1. 主从复制机制1.1 复制原理1.2 配置“一主多从”试验主从复制1&#xff09;为每个Redis进程提供配置文件2&#xff09;分别在不同客户端启动Redis服务3&#xff09;配置服务器成为某主机的下属从机 1.3 不同模式1.3.1 一主二仆1.3.2 薪火相传1.3.3 反客为主 1.4 哨…

在京东做外包的那些日子....

不知不觉已经在京东做了两年外包&#xff0c;最近收到不少朋友私信&#xff0c;说马上面临就业&#xff0c;在找工作的过程中&#xff0c;有不少软件测试的外包公司给我打电话让去面试&#xff0c;究竟要不要去&#xff1f;这篇文章给大家说说我这2年的外包经验&#xff0c;希望…

【Java基础学习打卡01】计算机概述

目录 引言一、计算机是什么&#xff1f;1.计算机vs计算器2.计算机定义 二、计算机发展简史三、计算机分类四、计算机基本工作原理1.冯诺依曼2.冯诺依曼原理 总结 引言 其实我们在学习Java编程之前应该要对计算机有所了解&#xff0c;这里的了解不是说我们日常接触电脑就算是了…

机器视觉初步2:机器视觉基础知识

本节概况 1.机器视觉的定义2.机器视觉系统的工作流程3.机器视觉的组成视场和最大像面 1.机器视觉的定义 什么是机器视觉&#xff0c;其中美国的一种定义为&#xff1a; “机器视觉是研究如何通过光学装置和非接触式传感器自动地接收、处理真实场景的图像&#xff0c;以获得所需…

前端使用tailwindcss 快速实现主题切换方案

使用Tailwind CSS在黑暗模式下为你的网站设计样式。 现在&#xff0c;黑暗模式是许多操作系统的第一流功能&#xff0c;为你的网站设计一个黑暗版本以配合默认设计&#xff0c;变得越来越普遍。 为了使这一点尽可能简单&#xff0c;Tailwind包括一个暗色变体&#xff0c;让你…

POWERBUILDER基础学习提纲

Chengg0769 2012年 版权信息保留&#xff1a; www.mis2erp.com http://blog.csdn.net/chengg0769 http://www.haojiaocheng.cc 这当时是给列给一位因伤休养的朋友。他有一段难捱的时间&#xff0c;想学习学习。这样复出之后也不至于工作无望或者浪费这段时间。 在SQL2000基础…