fail-fast机制是Java集合的一种错误机制。当多个线程对同一个集合进行操作时,可能就会产生fail-fast事件。比如当我们使用iterator去遍历集合的同时另一线程对该集合进行操作,就有可能抛出 java.util.ConcurrentModificationException异常,产生fail-fast事件。
1.fail-fast原理
我们通过ArrayList中的iterator源码剖析来发现Java是如何检测并抛出该异常的。
首先,ArrayList继承的 AbstractList中有一int字段modCount,我们在使用add、set等操作时,都会进行modCount++;
然后,我们看iterator源码
public Iterator<E> iterator() {
return new Itr();
}
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;
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];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
其中加粗部分是实现关键。expectedModCount保存了创建iterator时的mod值,以后每次遍历list的时候,都会比较expectedModCount和modCount是否相等,若不相等,则抛出异常。
2.解决方法
在多线程环境下,若要避开fail-fast机制,可以使用“java.util.concurrent包下的类”去取代“java.util包下的类”。如
将
private static List<String> list = new ArrayList<String>();
替换为
private static List<String> list = new CopyOnWriteArrayList<String>();
其主要实现原理其实就是简单的避开了modCount检查。