fail-fast 机制是Java集合(Collection)中的一种错误机制。 在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的结构进行了修改(增加、删除),则会抛出Concurrent Modification Exception(并发修改异常)。
所以,在多线程环境下,是很容易抛出Concurrent Modification Exception的,比如线程1正在对集合进行遍历,此时线程2对集合进行修改(增加、删除)。但是,单线程就不会抛出吗?很明显,单线程也会有类似的情况,比如main线程在遍历时对集合进行修改(增加、删除、修改),那么main线程就会抛出Concurrent Modification Exception异常。
因为我们是在遍历的时候对集合修改会发生fail-fast,遍历集合一般都用迭代器,那就先看看迭代器的源码:
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();
}
}
可以看到,罪魁祸首是checkForComodification这个方法,它是在modCount != expectedModCount的时候抛出了那该死的异常,而在next方法中第一句就是checkForComodification,所以遍历集合才会可能抛出并发修改异常。而且,在创建一个迭代器后,expectedModCount的初始值就是modCount了,对集合修改只会改变modCount,expectedModCount只会在迭代器的remove方法中被修改为modCount。
那么我们如何避免fail-fast抛异常呢?
1.如果非要在遍历的时候修改集合,那么建议用迭代器的remove等方法,而不是用集合的remove等方法。(老实遵守阿里巴巴java开发规范……)
2.如果是并发的环境,那还要对Iterator对象加锁;也可以直接使用Collections.synchronizedList。
3.CopyOnWriteArrayList(采用fail-safe)
提枪策马乘胜追击04-21 20:01
代码小兵92504-17 16:07
代码小兵98804-25 13:57
杨晶珍05-11 14:54