快速失败:
举个栗子:
public static void main(String[] args) { ArrayList<String> list=new ArrayList<>(); list.add("包龙星"); list.add("常威"); list.add("来福"); list.add("豹子头"); list.add(null); Iterator<String> iterator=list.iterator(); while (iterator.hasNext()){ String s=iterator.next(); if(s.equals("常威")); list.remove("常威"); } }
运行结果:
Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909) at java.util.ArrayList$Itr.next(ArrayList.java:859) at com.Collection.ArrayListTest.main(ArrayListTest.java:23)
jdk上异常解释:某个线程在 Collection 上进行迭代时,通常不允许另一个线性修改该 Collection。通常在这些情况下,迭代的结果是不确定的。如果检测到这种行为,一些迭代器实现(包括 JRE 提供的所有通用 collection 实现)可能选择抛出此异常。执行该操作的迭代器称为快速失败 迭代器,因为迭代器很快就完全失败,而不会冒着在将来某个时间任意发生不确定行为的风险。
也就是说当我们通过迭代器遍历集合时,如果直接通过集合增删了集合中的元素,就会抛出ConcurrentModificationException,称为快速失败!
源码分析:(注意重点红色标记)
Iterator部分源码:
private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; Itr() {} public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; }
checkForComdification方法
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
Arraylist的删除源码:
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
原理分析:迭代器第一次被调用时,会将一个modCount的值保存在expectedmodCount中,每当迭代器.next()操作遍历下一个元素之时,都会检查modCount变量是否为expectedmodCount值,集合在被遍历期间如果直接通过集合的方法改变集合内容,modCount++,进而导致下次进入.next()方法时匹配失败,抛出异常
ps:java.util包下面的所有的集合类都是快速失败的
安全失败:
采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。
原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception。
缺点:基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。
java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。