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