【Java】ArrayList线程不安全的坑

问题复现:

使用Java的steam().paralleStream(),foreach()方法向ArrayList添加数据,导致ArrayList中出现空值,代码如下:

    public static void main(String[] args) {
        List<Integer> a = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            a.add(i);
        }
        List<String> a1 = new ArrayList<>();
        a.parallelStream().forEach(x -> {
            a1.add(String.valueOf(x));
        });
        System.out.println(a1);
    }

输出结果:

[12, 6, 14, 5, 13, 8, 11, 9, 10, 7, 2, 17, 1, 4, 0, null, 19, 18, 16, 15]

如上,会出现null值

问题分析:

ArrayList通过size变量来控制当前数组元素的指针,代码:

    private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length) {
            elementData = this.grow();
        }

        elementData[s] = e;
        this.size = s + 1;
    }

    public boolean add(E e) {
        ++this.modCount;
        this.add(e, this.elementData, this.size);
        return true;
    }

如果此时出现多线程操作,例如Thread1获取到size=3,对数组的第三个元素进行赋值,此时Thread2获取到的size也等于3,也对数组第三个元素进行赋值,此时Thread1和Thread2都操作的第三个元素,然后此时Thread1进行size+1操作,Thread2进行加size+1操作,当前size值为5,但是实际数组中只有4个元素,有一个为null,因为Thread2本来应该对数组第4个元素赋值,因为线程不安全导致拿到的size值为3

解决方式:

1.使用Vector,所有方法用synchronized修饰,数组结构

2.Collections.sychronizedList(list)的方式获取一个list的代理,不限于ArrayList,可以实现任意list子类的同步,对大部分操作代码块进行加锁,例如:add,set,get,foreach等,不过迭代器操作未加锁,依旧是线程不安全的

3.使用Java集合的stream().map().collect(Collectors.toList())方法,利用Fork/Join的分治,每个线程会创建一个独立list进行操作,最后合并

4.CopyOnWriteArrayList,写操作单独创建一个list,读操作不加锁,适合读操作较多的场景

本文转载于网络 如有侵权请联系删除

相关文章

  • 舵机工作原理及程序[通俗易懂]

    大家好,又见面了,我是你们的朋友全栈君。舵机可以实时控制角度,广泛应用于航模、云台等等需要控制角度的场合舵机的工作原理:由pwm波进入内部电路产生一个偏置电压,触发电机通过减速齿轮带动电位器移动,使电压差为零时,电机停转,从而达到伺服的效果。舵机PWM的协议都是相同的,但最新出现的舵机可能不一样协议一般为:高电平宽度在0.5ms~2.5ms控制舵机转过不同的角度例如某一型号如下:对于不同的型号可以查找相关的舵机文档分析:在此以工作频率为50HZ,即周期为20MS的futabas3003舵机,和51单片机为主控芯片进行详细程序编写分析:有上图可知:不同宽度的脉冲可以产生不同的角度输出,可以利用定时器和IO口模拟产生PWM波,通过定时控制占空比从而达到角度的控制,理论上计算51可以产生us级别的脉冲,180/2000us=0.09,可以达到0.1左右的精度(实际我也没有试过)代码:下面是我写的以9°步进的程序,实际测试时只有16次为有效,估计是程序运行耗时造成的,因为我的定时器是以0.1ms溢出的。#include<reg52.h> unsignedcharcount;//0

  • Hadoop集群中的Mahout-distribution-0.7安装与配置

    Hadoop集群中的Mahout-distribution-0.7安装与配置系统配置:Ubuntu12.04hadoop-1.1.2jdk1.6.0_45Mahout是Hadoop的一种高级应用。运行Mahout需要提前安装好Hadoop,Mahout只在Hadoop集群的NameNode节点上安装一个即可,其他数据节点上不需要安装。1、下载二进制解压安装。到http://labs.renren.com/apache-mirror/mahout/0.7下载,我选择下载二进制包,直接解压及可。hadoop@ubuntu:~$tar-zxvfmahout-distribution-0.7.tar.gz2、配置环境变量:/etc/profile使环境变量生效:source /etc/profile3、启动hadoop4、mahout--help检查Mahout是否安装完好,看是否列出了一些算法   当然,这种方法并不准确,可以通过接下来的步骤进行验证。5、mahout使用准备a.下载一个文件synthetic_control.data,下载地址:http://archive.ics.uci.

  • 【Web开发】粒子插件vue-particles的使用

    今日在尝试开发一个登录界面时,发现背景是一张图片挺平平无奇,为了让界面更加炫酷,学习使用粒子插件vue-particles插件安装npminstallvue-particles--save复制在main.js内导入importVueParticlesfrom'vue-particles' Vue.use(VueParticles)复制在Login.vue内插入代码<vue-particles color="#409EFF" :particleOpacity="0.7" :particlesNumber="80" shapeType="circle" :particleSize="4" linesColor="#409EFF" :linesWidth="1" :lineLinked="true" :lineOpacity="0.4" :linesDistance="150&q

  • 12岁的B站,放慢脚步做社区

    配图来自Canva可画近日,随着12周年演讲的落幕,年满十二岁的B站似乎又重新站在了起跑线上。在对过去一年进行总结的同时,CEO陈睿也在演讲中再一次强调了B站的学习属性、ACG属性以及独特的内容生态。而且在演讲中,陈睿也表示社区的健康度要比规模化更加重要,另外“B站不是一个工具,而是一个社区。”的重新定义,也足以说明陈睿和B站的立场。不难看出,一个更加完整社区的构建,已经成为了B站下一阶段的新重点。构建社区的三角循环构建一个完整的社区,需要的是什么呢?对于B站来说,答案无非就是平台内容、up主以及用户这三个要素。而这三个要素也是B站在近几年得以迅猛成长的关键。不断的破圈行为让平台内容逐渐多元,吸引越来越多圈层的用户走入B站,为内容创作者up主带来成长和赚钱的机会,促进高质量内容产出,再一次充实平台内容的体量。这三个方面的良性循环,加速着B站的成长,然而构建一个社区虽然需要的也是这三点,但是却需要进行不同程度的深化。其一是内容层面的重新定义。过于娱乐化的内容,满足的是人们的瞬时快乐,但这种瞬时快乐和长久收获存在冲突,会影响到部分用户的留存以及看待平台的态度。而且想要构建一个平稳社区,光靠

  • 通用计数器的应用价值

    随着科学技术发展,一些应用系统,如大型通信系统,电力系统,特别是高速运动目标的跟踪定位系统,对时间间隔的测量精度提出了越来越高的要求,同时我国对时间间隔计数器的应用掌控性,更倾向于依赖于国产设备实现数字式频率计的研发。虽然通用计数器测量周期误差不可能从根本上消除,但通过对转换误差、量化误差及各类误差产生原因的深入分析,可以制定出相对有效的减小误差的方式,且随着电子计数器的不断改进与误差减小方法效用的不断提升,相信测量周期误差会被降至最低。通用计数器是很容易操作的,因为通用计数器的计算范围一般是从1到9万的。通用计数器一般有四个按键的,我们可以通过这四个功能按键进行设计和转换。使用通用计数器的时候要进行设置,因为通用计数器和普通的计数器是不一样的。智能计数器一般是有记忆功能的,所以我们在使用完之后最好是清除数据重新设置。国产频率计推荐型号是SYN5636型高精度通用计数器,是一款按照《JJG349-2014通用计数器检定规程》研发生产的高性价比的时频测试仪器。该通用计数器可选恒温晶振或者铷钟作为时基,标准产品配置的是温补晶振时基。同时具有测量和统计两大功能。测量频率、周期、频率比、输入电

  • 构建批流一体数据集成平台的一致性语义保证

    转自:未知的瞬间陈肃致力于企业级数据集成平台的研发。曾就职于中国移动研究院(用户行为实验室负责人)、亿瑞互动科技有限公司(技术VP)。对消息中间件、推荐系统等领域都有丰富的实践经验。拥有十项发明专利。 批量和流式是数据集成的两种任务形态。在实际应用中,批量和流式往往需要结合使用:前者处理历史数据,后者处理增量数据。数据同步的一致性语义保证是构建批流一体数据集成平台的基本要求。无论是批流切换,还是数据在流转环节中出现的运行异常,都不能影响数据的最终一致性。KafkaConnect作为一个被广泛应用的数据集成框架,只提供了数据同步端到端至少一次(atleastonce)的语义保证。我们在此框架之上,进一步实现了较为通用的精确一次(exactlyonce)语义保证。

  • Java多线程学习(二)——Thread类的方法使用

    Thread类的一些方法介绍1、currentThread()方法currentThread()方法可以返回代码被那个线程调用的信息。测试方法如下:publicclassMyThreadextendsThread{publicMyThread(){super();}publicMyThread(Stringname){super();this.setName(name);System.out.println("构造器中线程名字:"+Thread.currentThread().getName());} @Overridepublicvoidrun(){super.run();System.out.println("thisisMyThread");System.out.println("run方法中线程名字:"+Thread.currentThread().getName());} publicstaticvoidmain(String[]args){ //继承ThreadMyThreadmyThread=newMyThre

  • 国际波普大师RON ENGLISH携新作震撼来袭!

    【I♡UX】夏季论坛6月21日RonEnglish来袭,同时举办新作品世界首发大展!6月21日,国际级波普艺术家及潮玩教父RonEnglish将莅临【I♡UX】论坛进行演讲,同时举办新作品世界首发大展!见大咖,看新作!现场活动仅限报名用户参与!其他用户可通过订阅微信直播观看!(直播小程序在文章底部)35秒视频了解RonEnglishRonEnglishRonEnglish是美国当今最知名、最多产的艺术家之一,他在街头、博物馆、电影、书刊、电视中,以令人难忘的艺术作品轰动全球。Ron创造了POPaganda这个新词汇,来描述对于不同文化内容的标志性混搭,他还创造了炙手可热的众多原创角色,如麦胖(热门电影《超码的我》中的快餐吉祥物)、亚伯拉罕·奥巴马(美国第16任和第44任总统的结合体,引起媒体广泛讨论,并对2008年美国选举产生直接影响)等。通过多种艺术形式,如画作、广告牌、雕塑,角色等,RonEnglish巧妙地将令人惊叹的视觉效果与美国波普偶像破坏者的幽默暗示完美结合,成为世界公认的波普艺术家及潮玩教父。RonEnglish是一个多产的艺术家,从上世纪70年代开始,他便开始了街头艺术

  • Python学习笔记(十)

    程序练习1:#编写一个函数,分别统计传入字符串参数(可能不止一个参数)的英文字母、 空格、数字和其他字符的个数 #程序执行结果举例: #count('iloveyou','iloveyou,youlovetest123.com') #第一个字符串共有:英文字母8个,数字0个,空格2个,其他字符0个。 #第二个字符串共有:英文字母14个,数字3个,空格2个,其他字符2个。复制程序练习2:#编写一个函数,判断输入的字符串参数是否为回文联。 #回文联即用回文形式携程的对联,既可顺读,也可逆读。比如:上海自来水来自海上复制习题1参考答案:defcount(*param): length=len(param) foriinrange(length): letters=0 space=0 digit=0 others=0 foreachinparam[i]: ifeach.isalpha(): letters+=1 elifeach.isdigit(): digit+=1 elifeach=='': space+=1 else: othe

  • 最近工作中遇到ElasticSearch一些问题总结

    最近在使用ElasticSearch来查询我们的一些实时数据,中间也遇到不少的问题,今天在此简单总结记录一下。es的功能的确十分强大,大部分数据库能实现的需求,基本在es里面都能实现,当然两者都有一些特殊的功能,是双方不能实现或者不容易实现的。案例一:es单个字段分组后,分页是个小坑,因为分组结果里面,只提供了size来限制返回,并没有类似offset和limit的参数来支持分页,所以这个地方,一般提供的是topN的分组数据,如果想分页,只能自己在内存中实现。案例二:如下面的sql:在es多个字段分组后,排序都是组内有序,而不是整体数据有序,如果想实现整体数据有序,有两种解决办法可以解决:方法一:使用agg-script,使用script来实现聚合,这样唯一的缺点就是大数据量下,耗时比较高。方法二:使用copy字段,将多维字段,提供合并成一个字段,这样分组时候就可以直接使用这个字段进行分组,效率较高,但灵活性比较低,如果要是有7,8个字段都需要两两组合分组,那么索引里面的冗余字段就会多出好多个,索引体积的增大也会影响检索和聚合性能。当然上面的分组能处理,但是分页问题依然和案例一是一样的

  • 8个让程序员追悔莫及的职业建议

    8个让程序员追悔莫及的职业建议正如老牌Faces乐队的经典老歌《OohLaLa》中的歌词一样“IwishthatIknewwhatIknownowwhenIwasyounger”,我常常想,要是我早点知道这些建议就好了。回首往事,刚开始的时候我只是非常单纯地喜欢写代码,也不知道要规划自己的职业生涯以及如何与人相处。我常常懊悔,要是我能早点知道下面这8条简单又实用的技巧,那我能少走很多弯路,避免很多麻烦。 1.注重交际。 我以前特别专注于计算机,任何打搅我的人和事我都认为是不速之客。我承认那个时候我的反应有点过头,因为还是有很多值得认识的业界知名人士和值得相交的朋友,但是我却没有好好保存他们的名片。我从不刻意记他们的名字,也不与他们联系。如果需要找工作的时候,我只会去用户组看看。 我发现对于一些年轻的开发人员而言,找工作似乎是小菜一碟。但我不然——有很多时候,他们总是对我说,你是个开发人员,知道点基本的语法和如何搜索(我刚刚进入这一领域的时候,还没有谷歌),想要立即被聘用是远远不够的。甚至有的时候,实在没办法了,我只能没完没了地发邮件给猎头。这种类似的情况以前时有发生,我有心无力。 还

  • Mybatis用法功能

    packagecom.jsoft.test; importcom.jsoft.dao.UserMapper; importcom.jsoft.entity.User; importorg.apache.ibatis.session.SqlSession; importorg.apache.ibatis.session.SqlSessionFactory; importorg.apache.ibatis.session.SqlSessionFactoryBuilder; importorg.junit.After; importorg.junit.Before; importorg.junit.Test; importjava.io.InputStream; importjava.util.List; publicclassUserMapperTest{ //获取session SqlSessionsession; //在Test的方法执行之前执行的方法 @Before publicvoidbefore(){ //构建一个session工厂 //加载mybatis的主配置文件

  • 迎风而上:国产网卡再赋能

        据TECHCET数据显示,全球约45%-54%的半导体级氚气由乌克兰Ingas和Cryoin两家公司供应,而美国所需的氘气供应几乎全部来源于乌克兰。     俄罗斯是主要的钯供应商,满足全球约33%的需求。钯用于传感器和新兴存储器(MRAM)制造,并用作某些封装技术的电镀材料。     而在短期看,俄乌冲突或对半导体特种气体供应带来了一定影响。     这也愈发凸显了市场对供应链安全可控的诉求。     俄乌冲突再次体现出现代化战争已不再是单纯的军事战争,是科技、经济等多方面的角逐,以半导体为首的电子产业成为各个国家针锋相对、寸步不让的又一新战场,以俄乌冲突、新冠疫情、中美贸易战等为代表的“黑天鹅”事件或将重塑半导体供应链体系。    此背景下,世界各国将半导体产业上升到国家安全战略层面,中、美、欧、日、韩等纷纷出台大量相关政策支持产业发展。 &nbs

  • 【管理心得之六十】制定项目计划不是只有时间才做的一种点缀

    场景再现 ================== PM:老板,有两个消息,一个好消息,一个坏消息。想先听哪个? BOSS:好消息是什么? PM:○○公司同意了我们上月前提出的报价,并在下周同我们签订合同。 BOSS:不错,坏消息又是什么? PM:完成项目的时间表被压缩了,由于我们此次开发采用的是新技术,客户那里有几个部门对这项新技术感兴趣,想把这个试点项目尽快完成。 BOSS:这没什么? PM:我知道制定项目计划对项目来说是一个重要环节,但考虑到项目截至日期提前了,我就把做项目计划的时间略去了,现在已经开始工作了。 BOSS:没做项目计划,就开始着手作业了?开什么玩笑…… PM:我们真的没有时间了…………………….如果再不………………….恐怕…………………. ================== “没时间”似乎是万能的借口和说辞,在任何一种场合,只要你运用得当,都可以让你全身而退。 “喂,帮我买个盒饭回来呗?”“兄弟真对不起,我这赶时间,我得先走了!” “哥们,我这毕业设计就拜托给你了!”“真对不起,这几天公司总加班,真没时间帮你弄!” 话回场景“没有时间(或错过时间),真的就不需要制定

  • 单调栈求笛卡尔树

    笛卡尔树实际上是对一个整数序列建树,以这个整数序列为优先级,下标为数,建Treap 当优先级互不相同,笛卡尔树是惟一的 笛卡尔树可以通过区间rmq并维护一个位置在O(nlogn)的时间内求出来,不过我们有O(n)的做法 我们用一个单调栈维护当前优先级递减的序列,然后不断弹栈找到合适位置,把最后一个弹走的元素的父亲标记为她,再把她的父亲标记为栈顶就行 【题目描述】 Treap是一种平衡二叉搜索树,除二叉搜索树的基本性质外,Treap还满足一个性质: 每个节点都有一个确定的优先级,且每个节点的优先级都比它的两个儿子小(即它的优先级满足堆性质)。 不难证明在节点的优先级都事先给定且互不相同时,对应的Treap有且仅有一个。 现在,给定n个数和每个数对应的优先级,求出对应的以数的大小作为二叉搜索树比较依据的Treap的先序遍历结果。 对先序遍历的定义是:先访问根节点,再访问左子树,最后访问右子树。 【输入格式】 第一行一个数n表示数的个数。 第二行n个数表示每个数的大小。 第三行n个数表示每个数对应的优先级。 【输出各式】 一行n个数,表示Treap的先序遍历结果(对于每个节点,输出对应的数)

  • C# Memory Cache 踩坑记录

    背景 前些天公司服务器数据库访问量偏高,运维人员收到告警推送,安排我团队小伙伴排查原因. 我们发现原来系统定期会跑一个回归测试,该测运行的任务较多,每处理一条任务都会到数据库中取相关数据,高速地回归测试也带来了高频率的数据库读取. 解决方案1 我们认为每个任务要取的数据大相径庭,因此我们考虑对这个过程进行修改,加入MemoryCache把数据库中读取到的数据进行缓存. 整个修改非常简单,相信对常年混迹在博客园中的各位大佬来说小菜一碟,因此小弟不再叙述添加缓存的步骤细节. 从缓存的添加,代码提交,Teamcity编译通过,到测试环境,QA环境的安装无比流畅,一切显得如手到擒来. 嗯,优秀是一种习惯,没有一点办法. 人生如戏,当我们还沉浸在"我加的Cache不可能又BUG"的自信中时,QA传来噩耗,回归测试大量未通过.... 故障排查 之前习惯了使用Redis缓存,因此,常识告诉我们--- 在数据库中数据没有改动的前提下,加了缓存后读取的数据的效果和从数据库中读取的效果是一模一样的. 除非 ,,,  除非 这个常识是错误的.... 因此我们

  • SpringBoot整合swagger2

    1、导入依赖 <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency>复制 <!--lombok用于简化实体类的开发--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifa

  • 移动端页面适配———多方案解析

    移动互联网快速发展的今天,手机的种类和尺寸越来越多,作为前端的小伙伴们可能会越来越头疼,但又不得不去适配一款又一款的新机型。对于移动端适配,不同的公司、不同的团队有不同的解决方案。我在项目中也用了一部分解决方案,也看到了一些解决方案,对比下,总结一些自己的理解,希望对各位有帮助,找到最适合你们项目的适配方案。 下面是一些基础概念的讲解,帮助理解各种适配方案实现。 像素: 1、物理像素(设备像素) 屏幕的物理像素,又被称为设备像素,他是显示设备中一个最微小的物理部件。任何设备屏幕的物理像素出厂时就确定了,且固定不变的。 2、设备独立像素 设备独立像素也称为密度无关像素,可以认为是计算机坐标系统中的一个点,这个点代表一个可以由程序使用的虚拟像素(比如说CSS像素),然后由相关系统转换为物理像素。 3、设备像素比 设备像素比简称为dpr,其定义了物理像素和设备独立像素的对应关系 设备像素比=物理像素/设备独立像素 以iphone6为例: iphone6的设备宽和高为375pt*667pt,可以理解为设备的独立像素,而其设备像素比为2.固有设备像素为750pt*1334pt 复制 通过:w

  • js输出出现次数最多的字符和次数

    letstr='asfjasiofoivnoi'; functioncount(str){ letobj={}, arr=str.split(''); //遍历数组 arr.forEach(function(val,index){ //将数组的元素存入对象,初始值为1,如果后面遍历的元素之前已存在就+1 if(obj[val]){ obj[val]+=1; }else{ obj[val]=1 } }) //遍历对象中的属性(字符),值(出现的次数) letnum=0, res; for(letiinobj){ if(num<obj[i]){ //将最多的次数赋给num num=obj[i]; //最多次数的属性(字符串)赋给res res=i; } } console.log('最多的字符串是'+res+',出现次数是'+num); } count(str);复制   勤学似春起之苗,不见其增,日有所长;辍学如磨刀之石,不见其损,日所有亏!

  • Lua语言入门

    时间:2018-03-12记录:byzqy 1、lua语法基础:程序块 lua中连续的语句之间不需要使用分隔符,如果需要分隔,可以使用分号(;)进行分隔! lua中换行是不起作用的,会被当做一个空格来处理。 lua中在书写连续的语句时可以不换行,这也是合法的。但是由于可读性太差,所以不推荐! cmd中输入:lua然后点击回车,可以运行lua解释器。会显示lua版本信息,并进入lua命令提示符! 在cmd的lua命令提示符中,按下Ctrl+Z,可以退出lua命令提示符,回到cmd命令提示符! 使用记事本自定义一个fact.lua的文件,在文件中添加一个函数。然后在cmd中输入:lua-i“c:\demo\fact.lua”,将会先执行脚本定义的函数,然后才会进入到lua命令提示符交互模式!参数-i对调试和手动调试非常有用! --文件:fact.lua --函数:根据提示在命令提示符输入一个数字,然后计算这个函数的阶乘! functionfact(n) ifn==0then return1 else returnn*fact(n-1) end end print("entera

  • linux 中运行脚本的几种方式及其区别

    脚本程序在linux系统中运行时分为两种环境,一种是在当前shell(称为父shell)开启一个子shell环境,此shell脚本就在这个子shell环境中执行。shell脚本执行完后子shell环境随即关闭,然后又回到父shell中。第二种是当前shell中执行的,当我们需要利用脚本设置当前环境变量时,应该采取第二种运行脚本的方式。 下面分别介绍这两种运行方式的基本命令。 第一种:在子shell环境中运行 切换到shell脚本所在的目录(此时,称为工作目录)执行shell脚本: cd/data/shell./hello.sh ./的意思是说在当前的工作目录下执行hello.sh。如果不加上./,bash可能会响应找到不到hello.sh的错误信息。因为目前的工作目录(/data/shell)可能不在执行程序默认的搜索路径之列,也就是说,不在环境变量PASH的内容之中。查看PATH的内容可用echo$PASH命令。现在的/data/shell就不在环境变量PASH中的,所以必须加上./才可执行。 绝对路径执行 /data/shell/hello.sh 直接使用bash或sh

相关推荐

推荐阅读