黑马程序员技术交流社区

标题: 集合并发修改异常分析 [打印本页]

作者: 龚冼敏老师    时间: 2016-6-20 17:40
标题: 集合并发修改异常分析
本帖最后由 龚冼敏老师 于 2016-6-20 17:53 编辑

在学习集合这一块的时候, 如果我们在使用迭代器对集合进行遍历, 然后在遍历的过程中, 调用了集合中的remove方法对集合进行删除元素, 那么这个时候就会发生并发修改异常. 而细心的同学会发现, 在删除集合中的其他元素时都会有并发修改异常发生, 唯独在删除倒数第二个元素的时候不会有异常, 感觉非常的疑惑, 那么在这里跟大家一起分析一下, 这是什么原因.
      首先, 我们先要知道这个并发修改异常是怎么出现的.
在这里我们创建一个集合对象, 并存上几个元素, 为了方便, 我们就存字符串好了:




然后使用迭代器遍历这个集合, 并且在遍历的过程中调用集合的remove方法删除第一个元素:


再看运行结果:


这个时候就出现了并发修改异常!
通过查看java虚拟机给我们显示的异常信息可以看得出来, 发生异常的是ArrayList中的第831行的位置, 我们去查看一下源码:


我们发现, 第831行是迭代器中的next()方法调用了另一个方法checkForComodification(), 然后我们再进去看看这个方法做了什么事:


结果发现, 这个方法只是判断了一下两个变量, 如果这两个变量不相等就抛出并发修改异常, 所以我们想要知道, 这两个变量是代表的什么意思. 这两个变量都是显示为蓝色字体,那很明显它们都是成员变量, 那我们可以到成员位置找一下这两个变量:





在源码中我们找到了这两个变量, 需要注意的是, modCount是AbstractList中的一个成员变量,源码中的解释是, 该变量代表了该集合被修改的次数. 而expectedModCount是Iterator的一个实现类Itr中的一个成员变量, 这个变量在Itr创建对象时, expectedModCount=modCount, 所以expectedModCount也可以看成是集合修改的次数.
         既然这两个变量一开始是相等的, 而在checkForComodification()方法中的判断条件是当expectedModCount!=modCount时抛出并发修改异常, 那么肯定会有某些方法会操作这两个变量, 才会让它们两个不相等, 而我们之前所做的操作就是在遍历的过程中删除元素,所以,我们去看一下集合中的删除方法:


在删除方法里面又调用了fastRemove(index)这个方法, 我们进去看看:


fastRemove(index)这个方法中, 我们发现, 当集合删除一个元素时, modCount就会自增一次, 这个时候expectedModCount却没有发生变化, 结果就会抛出并发修改异常了.
         现在思路已经清晰了, 当迭代器进行遍历时, 集合中的修改次数modCount 会赋值给迭代器中的成员变量expectedModCount , 集合中的删除方法在删除元素时, 会对modCount 进行自增, 而 expectedModCount 却不会变化, 这时迭代器再去调用next() 方法取出元素时, 会先判断expectedModCountmodCount是否相等, 如果不相等就抛出并发修改异常,所以迭代器在遍历的过程中是不能调用集合中的remove方法删除元素的!
         但是问题来了, 为什么在删除集合中的倒数第二个元素的时候不会有问题呢?


这个代码竟然没有出错!!!
这不科学, 不是说在使用迭代器遍历的过程中删除元素会发生并发修改异常的吗?

上面我们已经分析过了, 在迭代器遍历时, 如果调用了集合的remove方法删除元素, 再去调用next()方法时, 就会发生并发修改异常, 而问题就在这里! 在这里我们不得不先说一下另一个方法, 就是迭代器中的hasNext() 方法, 看源码:


源码非常简单, 只要当前的索引跟集合的元素个数不相等, 那么就认为还有元素可以遍历.


cursorItr类中的成员变量, 初始化时并没有赋值, 所以默认值为0, 每遍历一个元素cursor就会跟着自增一次. 当遍历到最后一个元素时, cursor自增就会跟集合的长度相等了,此时迭代器就会认为已经遍历完所有元素了.


看上面的图, 当遍历到集合中的倒数第二个元素时, cursor3, 集合的size5, 然后通过集合中的remove()方法删除d元素, 此时集合的size变成了4, 在遍历下一个元素之前, cursor进行自增,变成了4, 当调用hasNext()方法判断是否有元素时, cursorsize都是4, 所以迭代器就会认为集合中的元素已经遍历完了, 就不会再调用next()方法了, 没有调用到next()方法自然就不会发生并发修改异常了!
          以上就是为什么在迭代器遍历时删除倒数第二个元素不会发生并发修改异常的原因了!


作者: 何亚辉    时间: 2016-6-20 21:01
老师,,   这个问题好像是我提出的, 我也想过说看源码, 但是源码跳过来跳过去的实在是看着痛苦..  你是怎么看的???
作者: 龚冼敏老师    时间: 2016-6-21 09:01
何亚辉 发表于 2016-6-20 21:01
老师,,   这个问题好像是我提出的, 我也想过说看源码, 但是源码跳过来跳过去的实在是看着痛苦..  你是怎么 ...

按ctrl键点进去看~
作者: 戎马生涯    时间: 2016-6-21 09:57
感谢分享~赞!!!
作者: 何亚辉    时间: 2016-6-21 10:16
龚冼敏老师 发表于 2016-6-21 09:01
按ctrl键点进去看~

缩噶!!!!!!   
作者: 王恩泽老师    时间: 2016-6-27 12:32
何亚辉 发表于 2016-6-20 21:01
老师,,   这个问题好像是我提出的, 我也想过说看源码, 但是源码跳过来跳过去的实在是看着痛苦..  你是怎么 ...


作者: 奕明传媒    时间: 2016-6-27 14:38
good good study,day day up,fighting,fighting,fighting!
作者: 逆风搁浅    时间: 2016-6-27 15:16
好好学习,天天向上
作者: sxj    时间: 2016-6-27 15:38
相信自己,加油!
作者: OmbreAmant丶C    时间: 2016-6-27 19:22
可以用ListIterator来避免!
作者: DDV    时间: 2016-6-27 19:53
学习了  好东西
作者: 雪莲    时间: 2016-6-27 20:17
新人报道
作者: Vision-V    时间: 2016-6-27 21:25
天啊 真是我们的基础班老师吗? 老师我在这啊,完全看不懂啊
作者: 云袭    时间: 2016-6-27 21:28
最后倒计时............
作者: 云袭    时间: 2016-6-27 21:33
学习.....
作者: zk19940530    时间: 2016-6-27 21:33
北京修正校区安卓49期的小伙伴有吗,我来签到了
作者: odada    时间: 2016-6-27 22:22
恩,先签个到,再仔细看。。。
作者: 杨海峰    时间: 2016-6-27 22:30
先签个到再说吧
作者: fanhongwei1105    时间: 2016-6-27 22:44
签个到~~~~~
作者: 黑夜的灬黑    时间: 2016-6-27 22:47
日常一签~
作者: 伯牙    时间: 2016-6-27 22:50
就差一分了 .
作者: 狂战斧    时间: 2016-6-27 22:51
基础班学了20天了  正纠结这个问题呢,,这个问题现在清楚啦   大大的攒啊
作者: denxinzlz610    时间: 2016-6-27 23:03
谢谢分享!!!!
作者: 王二小敲代码    时间: 2016-6-27 23:03
每日一签,上上签,,,,,,,,,
作者: 坑爹货    时间: 2016-6-27 23:16
签个到  接着看代码
作者: 15620608429    时间: 2016-6-27 23:28
明天继续上课
作者: 张雨    时间: 2016-6-27 23:43
没学呢啦啦啦啦啦
作者: 黑猫的消失    时间: 2016-6-27 23:49
学到了,以前都不知道并发修改异常还有特例
作者: TommingYu    时间: 2016-6-28 00:11
签个到 准备睡觉
作者: 初雪的忧伤    时间: 2016-6-28 00:19

感谢分享~赞!!!
作者: wanglun    时间: 2016-6-28 00:21
签个到 准备睡觉
作者: 初雪的忧伤    时间: 2016-6-28 00:22

感谢分享
作者: wanglun    时间: 2016-6-28 00:24
签个到 准备睡觉
作者: wanglun    时间: 2016-6-28 00:25
谢谢分享!!
作者: wanglun    时间: 2016-6-28 00:27
明天继续上课
作者: 梦回河口    时间: 2016-6-28 00:29
睡前来一发!
作者: 淹忆    时间: 2016-6-28 00:34
谢谢分享
作者: YC1992    时间: 2016-6-28 01:06
明天继续上课
作者: rogerpbj    时间: 2016-6-28 01:25
领教了~~
作者: James_01    时间: 2016-6-28 06:29
这算不算java设计上的一个bug呢
作者: wstm198907    时间: 2016-6-28 08:43
非常有用!!
作者: naonao1027    时间: 2016-6-28 10:35
收藏了,不错不错~赞
作者: perfect_class    时间: 2016-6-28 14:58
本帖最后由 perfect_class 于 2016-6-28 15:05 编辑

学习了,谢谢分享
作者: WDzyll    时间: 2016-6-28 17:52
受教了,今天真好碰到,感谢老师
作者: 云袭    时间: 2016-6-28 18:41
感恩.......!!!!!!
作者: songbing    时间: 2016-6-28 19:11
感觉这几天脑袋有点沉,得去打打球了!
作者: songbing    时间: 2016-6-28 19:14
君问归期未有期,巴山夜雨涨秋池。
作者: Ake丶    时间: 2016-6-28 21:07
玩了一周的论坛,原来这里才是学习的地方啊!!
作者: whcll    时间: 2016-6-28 21:30
签个到,希望再次能跟的大神门多学习!!
作者: lu598110900    时间: 2016-6-28 22:13
赞一个,顶一个
作者: zhoubinjian    时间: 2016-6-28 22:21
加油,加油
作者: xcjavaee    时间: 2016-6-28 22:29
忘了带鼠标回来
作者: silencelj    时间: 2016-6-28 22:52
加油加油加油加油加油!
作者: 往死里敲代码    时间: 2016-6-28 23:14
虽然看不懂,但是顶顶顶,以后肯定用得到
作者: 紫电_恶魔    时间: 2016-6-28 23:21
前来学习,
作者: 可可米修    时间: 2016-6-28 23:59
为什么ListIterator可以避免并发异常呢?
作者: YC1992    时间: 2016-6-29 00:48
签个到,希望再次能跟的大神门多学习!!
作者: zzw777    时间: 2016-6-29 15:35
学习,111111
作者: 精彩    时间: 2016-6-29 22:59
敲到  加油
作者: 黑暗中漫舞    时间: 2016-6-29 23:30
敲代码中
作者: 小成成    时间: 2016-6-29 23:56
敲代码中
作者: TommingYu    时间: 2016-6-30 00:10
签个到准备洗洗睡  明天自习  压力好哒

作者: wsl123456    时间: 2016-6-30 07:07
早早的签个到
作者: axiaowenjin    时间: 2016-6-30 07:45
敲代码中
作者: 月下夜风    时间: 2016-6-30 08:41
新的一天,加油!!!
作者: lybjfzdzy    时间: 2016-6-30 09:07
新的一天,加油,干掉其余的同学
作者: zzw777    时间: 2016-6-30 09:11
学习ing111
作者: 奕明传媒    时间: 2016-6-30 10:38
7月10日,黑马我老了
作者: jzp2015    时间: 2016-6-30 15:07
不能徘徊在选择中 奋斗
作者: 忆图灵    时间: 2016-6-30 15:25
给老龚一个技术分,诺给“一个技术分”,O(∩_∩)O哈哈~
作者: guowei    时间: 2016-6-30 16:39
赞一个`````
作者: yaozhilin    时间: 2016-6-30 17:24
好好学习,天天向上!
作者: 梦想天空分外蓝    时间: 2016-6-30 18:23
good good study
day day    up up up   u  u     u  ..................
作者: MartinYu    时间: 2016-6-30 18:49
我来了,大家好啊!
作者: axiaowenjin    时间: 2016-6-30 18:50
大家好,大家哈皮啊,
作者: 云袭    时间: 2016-6-30 18:51
紧迫感呢?.........
作者: 新兵蛋子    时间: 2016-6-30 18:58
学习起来好吃力,代码写不清楚,怎么办?
作者: 眉似新月弯弯    时间: 2016-6-30 19:47
感觉压力越来越大了,加油.
作者: 新兵蛋子    时间: 2016-6-30 19:52
大家好,很高兴来到这里.
作者: 新兵蛋子    时间: 2016-6-30 19:55
代码格式记不住,总是报错怎么办?
作者: 新兵蛋子    时间: 2016-6-30 19:56
加油加油
作者: 麦子守望星空    时间: 2016-6-30 20:07
大家好,我是一名新兵

作者: 你挺好的吧    时间: 2016-6-30 20:16
刚把得刚把得
作者: DDV    时间: 2016-6-30 20:30
学习了 一起努力
作者: 代码是什么    时间: 2016-6-30 20:42
学习了,复习一下,不错.
作者: 王旭VH    时间: 2016-6-30 20:59
这个直播挺好的
作者: 雪莲    时间: 2016-6-30 21:06
挺好的  不错
作者: guiliujun    时间: 2016-6-30 21:42
签到,还没学到这里,可以提前看看

作者: CGTAZQ    时间: 2016-6-30 23:39
期待期待

作者: syinys7    时间: 2016-7-1 01:11
点招加油!
作者: 15242694137    时间: 2016-7-1 05:54
努力学习在见到中
作者: 奕明传媒    时间: 2016-7-1 08:59
黑马,我来了!
作者: aidaima520    时间: 2016-7-1 09:06
赞一个``````
作者: zzw777    时间: 2016-7-1 09:14
早起学习1111
作者: tuijun101112    时间: 2016-7-1 20:00
先收藏.在点赞,日后用
作者: 清凉    时间: 2016-7-4 08:16
写的很好,支持一下
作者: 清凉    时间: 2016-7-5 07:58
好棒,支持一下
作者: zhangceven    时间: 2017-1-4 21:32
我的天呐

作者: newu    时间: 2017-1-4 23:05
最后倒计时
作者: zhoubinjian    时间: 2017-1-8 10:57
感谢分享。。




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2