多线程并发修改异常(ConcurrentModificationE)产生的原因以及解决方案

多线程并发修改异常(ConcurrentModificationE)产生的原因以及解决方案并发修改异常一什么是并发修改异常(ConcurrentModificationException)二并发修改异常(ConcurrentModificationException)产生的原因1mo

一 什么是并发修改异常(ConcurrentModificationException)

当我们对集合进行迭代(遍历)的时候,同时(并发)对集合进行修改,就会产生并发修改
比如说遍历一个集合时,同时对其中一个元素删除,修改等等操作。
或者 list 在多线程情况下 既有读又有写,报出ConcurrentModificationException问题,
概括来说分为两种情况
1一边遍历集合,而另一边在修改集合时,会报ConcurrentModificationException错误
2在多线程进行插入操作时,由于没有进行同步操作,容易丢失数据,因此会报ConcurrentModificationException错误。
代码案例(来源于尚硅谷)

public class LThread { 
   
    public static void main(String[] args) { 
   
       List list=new ArrayList();//创建数组
        for ( int i = 0; i < 30; i++) { 
   
            int x=i;
            new Thread(()->{ 
   
                list.add(UUID.randomUUID().toString().substring(0,8));//往数组里面放数据
                System.out.println(list);
            }).start();
        }
    }
}

在这里插入图片描述

二并发修改异常(ConcurrentModificationException)产生的原因

点进报错提示
在这里插入图片描述
**发现是 modCount != expectedModCount,返回ConcurrentModificationException **

1 modCount是什么意思?

The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results
这个数是结构化修改的次数,结构修改是指改变列表的大小,或者以一种可能产生错误结果的方式扰乱列表。

2 expectedModCount是什么意思?

在这里插入图片描述
期望的修改次数

3 modCount != expectedModCount的原因

分析源码
在这里插入图片描述
在这里插入图片描述

显而易见 当添加add或删除元素remove来修改集合,那么modCount将更改,赋值给expectedModCount。
当多线程时,有的线程没写进去集合的size(大小)没变,有的线程写进去集合的size(大小)变了,导致计数器modCount的值发生了改变并不是有序+1,但是往出输出时,导致了expectedModCount与modCount两个计数器不相同,于是返回ConcurrentModificationE错误。

在这里插入图片描述
在这里插入图片描述
和预期的结果 差别十分大,有的线程刚改变完modCount,而另一个线程要输出改变前后的modCount。

三解决并发修改异常的方案

1使用 Vector(同步的)

在这里插入图片描述

为什么使用Vector

Vector是线程安全的容器可以替换ArrayList,只不过有点古老。也可以理解为每一个操作都加有synchronized,但是严重影响效率。

在这里插入图片描述

每个线程使用Add时,都会添加synchronize 其他线程无法在调用add方法,解决并发修改的问题

2 使用Collections.synchronizedList

Collections.synchronizedList 的实现原理

在这里插入图片描述

为什么使用Collections.synchronizedList

返回指定列表支持的同步(线程安全)列表. 在 List的操作加了一层 synchronize 同步控制,但是对于 使用 Iterator 遍历列表时,Collections.synchronizedList 可能发生ConcurrentModificationE。
在这里插入图片描述

3 使用 CopyOnWriteArrayList (读写分离的思想)

为什么使用 CopyOnWriteArrayList

源码分析 add、rome元素时,上锁保证同一时刻最多只有一个线程向list中添加元素,肯定是线程安全的, 同时add、rome都是不会去修改
原数组的,所以modCount 是不会去被其他线程改变的。

在这里插入图片描述

4 CopyOnWriteArrayList 在数组的迭代时也可防止并发修改异常

public class ModificationException { 
   
    public static void main(String[] args) { 
   
        List<String> list =new ArrayList();
        list.add("m");
        list.add("y");
        list.add("t");  //Arraylist 经常见的需求 迭代 找到一个数然后去掉
        for (String str : list) { 
   
            if ("t".equals(str)) { 
   
                list.remove("t"); //两个计数器不相同 报错误
            }
            System.out.println(list);
        }
    }
}

在这里插入图片描述
使用 CopyOnWriteArrayList
在这里插入图片描述

今天的文章多线程并发修改异常(ConcurrentModificationE)产生的原因以及解决方案分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/63718.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注