Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 1147|回复: 0

[默认分类] Exception in thread "Thread-1" java.util.ConcurrentModificationException 异常原因和解决方法

[复制链接]
  • TA的每日心情
    开心
    2021-12-13 21:45
  • 签到天数: 15 天

    [LV.4]偶尔看看III

    发表于 2020-8-8 10:15:11 | 显示全部楼层 |阅读模式
    基本上所有的集合类都会有一个叫做快速失败的校验机制,当一个集合在被多个线程修改并访问时,就会出现ConcurrentModificationException 校验机制。它的实现原理就是我们经常提到的modCount修改计数器。如果在读列表时,modCount发生变化则会抛出ConcurrentModificationException异常。这与线程同步是两码事,线程同步是为了保护集合中的数据不被脏读、脏写而设置的。

    1、单线程环境下的异常重现

    1.     public static void main(String[] args)  {
    2.         ArrayList<Integer> list = new ArrayList<Integer>();
    3.         list.add(2);
    4.         Iterator<Integer> iterator = list.iterator();
    5.         while(iterator.hasNext()){
    6.             Integer integer = iterator.next();
    7.             if(integer==2) {
    8.                 list.remove(integer);
    9.                 //iterator.remove();   //正确写法
    10.             }
    11.         }
    12.     }
    复制代码


      在while(iterator.hasNext()) 循环遍历时,只允许删除ArrayList 内部的  elementData[ ] 的最后一个元素,而不允许从中间删除。
      在 iterator.next()  的源码中,会首先执行方法:checkForComodification();  该方法:

    1.     final void checkForComodification() {
    2.             if (modCount != expectedModCount)
    3.                 throw new ConcurrentModificationException();
    4.         }
    复制代码


      只有当两个变量值相等时才不会报错。而 list.add(2)操作和 list.remove(integer); 操作会使 modCount++;   但是变量 expectedModCount 是内部类的 Itr 子类 的 变量,该子类的实例化方法是:list.iterator();  实例对象时赋初始值:
    int expectedModCount = modCount;   所以,不允许在 while(iterator.hasNext())  循环方法内 有list.add(2)操作和 list.remove(integer);操作,否则会导致expectedModCount != modCount ,进而导致  throw new ConcurrentModificationException();
    1. 2、多线程下的问题重现:
    复制代码


    1. public class Vector {
    2.     public static void main(String[] args) {
    3.         final List<String> tickets = new ArrayList<String>();
    4.         for(int i=0;i<100000;i++){
    5.             tickets.add("火车票"+i);
    6.         }
    7.         Thread returnThread = new Thread(){
    8.             @Override
    9.             public void run() {
    10.                while (true){
    11.                    tickets.add("车票"+ new Random().nextInt());
    12.                }
    13.             };
    14.         };
    15.         Thread saleThread = new Thread(){
    16.             @Override
    17.             public void run() {
    18.                 for (String ticket : tickets){
    19.                     tickets.remove(0);
    20.                 }
    21.             };
    22.         };
    23.         returnThread.start();
    24.         saleThread.start();
    25.     }
    26. }
    复制代码


      
      有可能有朋友说ArrayList是非线程安全的容器,换成Vector就没问题了,实际上换成Vector还是会出现这种错误。
      原因在于,虽然Vector的方法采用了synchronized进行了同步,但是实际上通过Iterator访问的情况下,每个线程里面返回的是不同的iterator,也即是说expectedModCount是每个线程私有。假若此时有2个线程,线程1在进行遍历,线程2在进行修改,那么很有可能导致线程2修改后导致Vector中的modCount自增了,线程2的expectedModCount也自增了,但是线程1的expectedModCount没有自增,此时线程1遍历时就会出现expectedModCount不等于modCount的情况了。
      因此一般有2种解决办法:
      1)在使用iterator迭代的时候使用synchronized或者Lock进行同步;
      2)使用并发容器CopyOnWriteArrayList代替ArrayList和Vector。
    [code][/code]
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-1 01:12 , Processed in 0.348687 second(s), 37 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表