写入null_ArrayList并发写出现Null值

ArrayList并非线程安全的容器,这一点大家可能都非常清楚,但是在并发写入的情况下,不安全的情况具体有哪些,大家是否很清楚呢?本篇文章重点聊一下出现null的情况,然后对于其他并发写的安全做一个简单的叙述

我们看下面的代码,打印List的元素数量以及打印存储的元素

        List list = new ArrayList<>();        for (int i=0;i<10;i++) {            int finalI = i;            new Thread(()->{                list.add(finalI +1);            }).start();        }        System.out.println(list.size());        System.out.println(list.toString());

最理想的情况下,打印结果应该如下:

10[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

但是有可能出现一些其他问题,就像下面结果List元素出现null值的结果

10[null, 1, 3, 4, 5, 6, 7, 8, 9, 10]或者10[null, 2, 3, 4, 5, 6, 7, 8, 9, 10]或者10[null, null, null, 1, 5, 6, 7, 8, 9, 10]......

在我看百度看到的所有答案中,关于并发写出现Null值,几乎都是将原因归咎到add方法中的size++上,这里我个人认为这种回答应该是错误的,出现null值的原因应该是扩容所造成的。

 public boolean add(E e) {        ensureCapacityInternal(size + 1);        elementData[size++] = e;}

首先说一下为什么我觉得网上的答案是错误的,我们模拟add方法,然后使用javap命令拿到class的字节码看一下:

#### Java程序int size = 0;int[] elementDate = new int[5];public void add() {        elementDate[size++] = 10;}#### Javap 得到的字节码    public void add();  Code:       0: aload_0       1: getfield      #3 // Field elementDate:[I       4: aload_0       5: dup       6: getfield      #2// Field size:I       9: dup_x1      10: iconst_1      11: iadd      12: putfield      #2// Field size:I      15: bipush        10      17: iastore      18: return

在add方法的字节码中,通过getfield拿到elementDate数组放入栈顶(操作数栈),然后dup命令复制栈顶的数组并将复制值压入栈顶,然后再通过getfield获取size数值,下一步dup_x1命令会将栈顶的数值size复制两份,并将两个复制值压入栈顶,然后iconst_1命令将数值1压入栈顶,再使用iadd命令对栈顶的两个元素进行相加,并通过putfield将size更新,最后iastore更新数组(因为dup_x1复制了两份,所以数组的索引仍然是更新前的size)。大家可以好好想一下这个操作,无论size++多么不安全,因为索引复制两份被保存的操作数栈中,所以不可能在list中出现null值,只会出现覆盖的可能。

如果大家理解了上面的过程,我们思考下为什么null值出现了呢?由于ArrayList是基于数组实现,由于数组大小一旦确定就无法更改,所以其每次扩容都是将旧数组容器的元素拷贝到新大小的数组中(Arrays.copyOf函数),由于我们通过new ArrayList<>()实例的对象初始化的大小是0,所以第一次插入就会扩容,由于ArrayList并非线程安全,第二次插入时,第一次扩容可能并没完成,于是也会进行一次扩容(第二次扩容),这次扩容所拿到list的elementDate是旧的,并不是第一次扩容后对象,于是会因为第一次插入的值并不在旧的elementDate中,而将null值更新到新的数组中。这里我们举一个详细的例子:

现在有线程A和B分别要插入元素1和2,当线程A调用add方法时size是0,于是会进行一次扩容,此时线程B调用add方法时size仍然是0,所以也会进行扩容,假设此时线程A比线程B扩容先完成,此时list的elementDate是新的数组对象(由线程A构建),然后开始执行elementDate[size++] = 1的程序,这个过程中线程B扩容拿到的数组仍然是旧的elementDate,于是线程B构造一个新的数组(数据全部为null),然后使list的elementDate指向线程B构造的对象,那么线程A之前构造的elementDate也就被丢掉了,但是由于size已经自增,所以线程B会在索引为1的位置赋予2,那么此时数组元素就成了[null,2],当然如果线程B扩容比线程A先完成那么就可能为[null,1]。

大家如果在初始化的时候就已经开辟好足够大的容量,那么就不会出现上面的问题,关于上面的解释大家可以作为参考,因为不同的编译器可能javap得到的字节码可能会不同吧(这里我编译结果是size被复制两份,然后使用其中的一份加一更新到size中,然后用复制的另一份作为索引更新数组,但是网上得到信息大家都认为是数组先赋值,然后size自增)。

除了上面元素为null的情况外,还会有其他错误

  • 数量错误,集合数据正确
9[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
f28e4123825e22a2e68a9e8626545edc.png
9[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

大家是不是第一反应是不是觉得这种结果是由ArrayList本身的不安全特效造成的呢?实际上这种结果和ArrayList本身没有关系,只是因为我们打印不具有原子性所造成的。因为我们启用了多线程,主线程调用size方法时,可能多线程内部对list还在继续执行增加元素的操作,当主线程调用toString方法时,多线程已经执行完毕,所以元素数量正确,当然也有可能你调用toString方法时,多线程仍然未执行完,此时size和toString结果都不正确,如下:

8[1, 2, 3, 4, 5, 6, 7, 8, 9]
  • 覆盖,这种情况的原因在上面的分析中以及提到,因为size++并不是原子性的,所以可能线程A自增的时候,线程B也进行一次自增,但是两次自增的结果是一样的,所以先完成的线程更新的数据会被后完成的线程覆盖掉

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

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

相关文章

c语言函数调用数组_第七讲:C语言基础之函数,第二节,实现汉诺塔

本文约2200字&#xff0c;主要讲了C语言基础之函数&#xff0c;递归&#xff0c;数组作为函数参数以及案例、练习题等。带你进入C语言的世界&#xff0c;入门C语言&#xff0c;后边将持续更新。可以收藏学习。想了解C语言基础之函数&#xff0c;函数的基本概述&#xff0c;函数…

仍然报错_only_full_group_by配置,竟让所有应用报错?

推荐学习周一福利到&#xff01;献上“独家全新”MySQL进阶套餐&#xff0c;简直就是血赚 全网独家的“MySQL高级知识”集合&#xff0c;骨灰级收藏&#xff0c;手慢则无 1. 踩坑经历一个很平常的下午&#xff0c;大家都在埋头认真写bug呢&#xff0c;突然企业微信群里炸锅了&a…

根据录入的计算公式计算_污水处理工程设计入门(10)—堰口计算

呓语&#xff1a;虽然现在很多人会推送很多的快速计算表格&#xff0c;但是我并不想授人以鱼。我希望每一个想学习想入门做污水处理工程设计的人先看懂如何设计&#xff0c;再去想如何快速计算&#xff0c;再去想怎么改进。再者&#xff0c;想想有一天你当领导审图了&#xff0…

关于使用在webforms里使用routing遇到的问题

看了重典的两篇文章 System.Web.Routing入门及进阶 上篇 System.Web.Routing入门及进阶 下篇 仿照其中的进行了操作&#xff0c;发现怎么不都起作用&#xff0c;非常奇怪&#xff0c;搜了好多才找到了解决方案 是在MSDN上找到的&#xff0c;如下&#xff1a; To configure …

[导入]【翻译】WF从入门到精通(第十章):事件活动

摘要: 学习完本章&#xff0c;你将掌握&#xff1a;1.使用HandleExtenalEvent活动创建特定的事件处理程序2.在你的工作流中使用Delay活动3.在你的工作流中使用EventDriven活动4.在你的工作流中使用Listen活动5.理解EventHandlingScope活动在活动并发执行的情况下是怎样监听事件…

mysql查询返回xml格式_MySQL数据库查询操作XML的经验分享

mysql里面有内置的操作xml的函数。分别是ExtractValue()和UpdateXML()函数。语法&#xff1a;1EXTRACTVALUE (fiedname, XPathstring);第一个参数&#xff1a;fiedname是String格式&#xff0c;为表中的字段名第二个参数&#xff1a;XPathstring (Xpath格式的字符串) &#xff…

wordpress发布模块_如何用WordPress打造出一个类似知乎的问答站点

像打造一个像知乎这样的在线问答社区吗&#xff1f;问答网站非常有意思&#xff0c;而且用户活跃度也高&#xff0c;有很多非常有用的信息。在这篇文章中&#xff0c;我们将向你展示如何在没有任何编程经验的情况下利用WordPress快速打造一个问答类网站。你可以将整个网站做成问…

mysql 密码sha256_MySQL5.6启用sha256_password插件

一、背景&#xff1a;使用MySQL5.6过程中&#xff0c;发现默认的加密插件为mysql_native_password。而sha256_password的安全程度要比mysql_native_password高&#xff0c;尝试切换为sha256_password。二、配置过程&#xff1a;资料&#xff1a;1、从MySQL官网查询到服务器端sh…

ftp无法连接虚拟机_一步步编写操作系统4 安装x86虚拟机 bochs

本节内容摘自《操作系统真象还原》&#xff0c;请大家支持正版Bochs下载安装在完成了linux发行版的安装后&#xff0c;现在到了安装bochs的环节&#xff0c;这是我们的操作系统最终的宿主机。由于我的工作是运维&#xff0c;所以练就了任何软件包都要从源码安装的“陋习”&…

python语言用什么关键字来声明一个类_Python语言和标准库(第三章:类和对象)...

python如何将函数和数据整合在一起&#xff0c;并且通过一个对象的名称访问它们。 如何和为什么使用类与对象&#xff0c;以及他们如何使编程人员易于多种情形下编写和使用程序。 3.1考虑编程 现在要在python中创建一个对对象的描述&#xff0c;您已有足够的只是获得两个视图。…

mysql主从1594错误_3分钟解决MySQL主从1594错误

3分钟解决MySQL主从1594错误简介Part1:写在最前1594这个错误看起来挺严重的&#xff0c;会提示你binlog文件或者Relay log损坏了,例如binary log is corrupted、relay log is corrupted之类的看起来很吓人是吧&#xff0c;多数是由于掉电引发的&#xff0c;这也说明了机房配备U…

ci框架 mysql 超时时间_mysql 字符集和校验规则( CHARSET amp; COLLATE)

我们首先看下常见建表语句&#xff1a;mallAllGoodsCREATE TABLE qywl.mallAllGoods ( id varchar(64) NOT NULL COMMENT 主键id,userId varchar(64) NOT NULL DEFAULT COMMENT 发布人id,storeId varchar(64) NOT NULL DEFAULT COMMENT 发布商号id,data varchar(1000) NOT NU…

SQL 附加数据库时出现 无法打开物理文件错误

错误信息: 执行 Transact-SQL 语句或批处理时发生了异常。 (Microsoft.SqlServer.ConnectionInfo) ------------------------------ 无法打开物理文件 "D:\SqlDataBase\Test.mdf"。操作系统错误 5:"5(拒绝访问。)"。 (Microsoft SQL Server&#xff0c;错误…

Flash网页游戏辅助工具制作简析

《热血三国》好像是比较热&#xff0c;玩的人也挺多的&#xff0c;年前一个朋友希望能让我写一个这个游戏的外挂&#xff0c;也出于无聊&#xff0c;所以去玩了一下&#xff0c;谁知道一玩就有点喜欢这个游戏了&#xff0c;当然玩归玩&#xff0c;东西还是要做地&#xff0c;当…

sqlserver2008导出mysql_SQLserver 2008将数据导出到Sql脚本文件的方法

请看下面的操作图解。1.使用Sql Server Management Studio 2008 连接数据库。2.选中要导出数据的数据库节点&#xff0c;点鼠标右键&#xff0c;在菜单中选择“任务”->“生成脚本”&#xff0c;如图&#xff1a;3。在弹出的界面中&#xff0c;点2次“下一步”进入如图界面中…

opencv配置_Opencv在vs2012下的配置

别问我为什么记录这么老的版本&#xff0c;问就是因为老师推荐和因为怕自己再次把小米的搜索框给忘记了&#xff0c;又找个好几十分钟的【环境变量】。另外&#xff0c;要敲敲自己的脑袋&#xff0c;作为小米电脑的老用户怎么可以把搜索框给忘记了呢&#xff1f;第一步&#xf…

php7安装mysqli扩展_Ubuntu14版本下无法使用php7.2版本的bcmath扩展

在使用如下命令安装bcmath扩展的时候sudo apt-add-repository ppa:ondrej/phpsudo apt-get updatesudo apt-get install php7.2-bcmath提示如下信息Reading package lists... DoneBuilding dependency tree Reading state information... DoneE: Unable to locate packag…

input 0.1无法相加_你真的知道0.1+0.2为何不等于0.3吗?

打开chrome控制台&#xff0c;给一个特别简单的输入如下&#xff1a;0.1 0.2 // 0.30000000000000004 复制代码不知道你有没有吃惊&#xff0c;这么简单的一个计算&#xff0c;无论在js中还是在python中&#xff0c;都不是准确的0.3&#xff0c;这是为什么呢&#xff1f;缘起要…

python图像增强_Python图像的增强处理操作示例【基于ImageEnhance类】

本文实例讲述了Python图像的增强处理操作。分享给大家供大家参考&#xff0c;具体如下&#xff1a; python中PIL模块中有一个叫做ImageEnhance的类&#xff0c;该类专门用于图像的增强处理&#xff0c;不仅可以增强&#xff08;或减弱&#xff09;图像的亮度、对比度、色度&…

python 二项分布_二项分布的理论基础、应用及Python实践

二项分布是概率统计中非常基础、非常实用的一种分布&#xff0c;可以说它在我们的生活中无所不在。它说明了这样一种现象&#xff1a;在给定的试验次数中&#xff0c;某一结果会发生多少次。比如&#xff1a;这个月有多少天会刮北风&#xff1f;今年有多少天会下雨&#xff1f;…