栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

并发修改异常
  • 一 什么是并发修改异常(ConcurrentModificationException)
  • 二并发修改异常(ConcurrentModificationException)产生的原因
    • 1 modCount是什么意思?
    • 2 expectedModCount是什么意思?
    • 3 modCount != expectedModCount的原因
  • 三解决并发修改异常的方案
    • 1使用 Vector(同步的)
      • 为什么使用Vector
    • 2 使用Collections.synchronizedList
      • Collections.synchronizedList 的实现原理
      • 为什么使用Collections.synchronizedList
    • 3 使用 CopyonWriteArrayList (读写分离的思想)
      • 为什么使用 CopyOnWriteArrayList
    • 4 CopyonWriteArrayList 在数组的迭代时也可防止并发修改异常

一 什么是并发修改异常(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 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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/282560.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号