并发异常ConcurrentModificationException
- 首先要了解什么是并发异常?(从源码的角度分析查看)
- 怎样才能产生并发异常?
- 找出解决办法
一、什么是并发异常?
-
从字面意思来看: 同时发生的修改异常
Concurrent——同时发生的,并存的
Modification——修正,改正,变更
Exception——异常 -
JDK源码解读:
上面一大串英文解释为:(来自搜狗英文转汉语翻译!)
此异常可能由检测到并发的方法引发对象的修改,当这种修改是不允许的。
例如,通常不允许一个线程修改集合,而另一个线程正在迭代它。总的来说,研究的结果:在这些情况下迭代是未定义的。一些迭代器实现(包括所有通用集合实现的实现)如果这种行为是检测到。这样做的迭代器称为“fair-fast快速失效迭代器”
因为它们迅速而干净地失败了,而不是冒武断的风险,未来不确定时间的非确定性行为。请注意,此异常并不总是表示某个对象已被“不同”线程同时修改。如果单线程发出一系列违反,在对象收缩时,该对象可能引发此异常。
例如,如果一个线程在集合被修改时直接修改集合,用快速失效迭代器迭代集合,迭代器会抛出这个异常。
请注意,通常不能保证快速失效行为,快速故障操作尽最大努力抛出{ @代码并发修改异常}。因此,编写一个依赖于此的程序是错误的其正确性异常: < i > { @代码并发修改异常}应该只用于检测错误。
看了一遍,还是很懵,那就从实例中分析一下由来吧!
作者对ConcurrentModificationException的描述是:
常见于集合:Collection、Iterator、ListIterator、Vector、LinkedList、HashSet、HashTable、TreeMap、AbstractList。
二、怎么才能产生并发异常?
我以ArrayList为例,用Iterator迭代器进行遍历,在遍历中使用了一次对象的remove()方法。
public class ArrayListIterator219 {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(1);
arrayList.add(4);
arrayList.add(6);
arrayList.add(2);
//迭代器iterator遍历
Iterator<Integer> iterator = arrayList.iterator();
while (iterator.hasNext()) {
Integer value = iterator.next();
if (value == 4) { //删除值为4的元素
arrayList.remove(Integer.valueOf(4));
}
iterator.remove(); //迭代器提供的删除方法!
System.out.print(value + " ");
}
就抛出了如下图所示的并发异常问题,戳开源码看一看:
873行的异常:
909行的异常:
对上面两个图进行分析:
函数checkForComodification()检验集合是否被修改过:
908行出现了一个以前没有分析过的变量modCount??这是什么?
如果这个值不等于ExceptedModCount,就会抛出并发异常。在源码中找找modCount
发现在定义属性的时候,有一个被保护的变量modCount。它的注释为↓↓↓
大概意思是:此列表在结构上被结构修改的次数,结构修改是那些改变列表大小的修改,或者以某种方式干扰列表,以至于进程中的迭代可能会产生不正确的结果。
结构性修改又是什么?是指add()、remove()、clear()、addAll()等增、删集合元素的动作。每当执行一次,modCount都会+1。来记录当前集合被操作的次数
873行异常中:
所以总结起来引起并发异常的原因是:
集合本身的增删改会引起modCount版本号的数值改变,操作一次版本号+1。
在迭代器中会将modCount版本号进行备份,成为迭代器的版本号expectedModCount,如果再次对集合进行操作时,集合的版本号发生改变,迭代器的版本号并不发生改变,最后会判断两个版本号是否一致,不一致则会抛出并发异常。此时产生fair-fast 事件
三、解决办法
既然用迭代器会产生并发异常,那我们就不要用迭代器去遍历集合就不会产生异常了啊!此时for each也不行,因为它底层也是用迭代器来实现的,用for循环来遍历。
for (int i = 0; i < arrayList.size(); i++) {
System.out.print(arrayList.get(i) + " ");
}
System.out.println();*/