fail-fast机制
集合类的fail-fast机制是指集合被多个线程操作时,导致iterator中的数据改变从而抛出异常。
比如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。
实例分析
使用ArrayList的iterator进行遍历的时候,下面是ArrayList的iterator的next()方法:
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];
}
checkForComodification()方法表示检测当前ArrayList有没有被其他线程修改,具体原理是通过ArrayList的当前modCount与之前的modCount进行对比(ArrayList没被操作一次,modCount值都会进行一次加一操作)。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
ArrayList为什么需要进行checkForComodification这个检测呢?其原因是iterator是通过cursor进行遍历的,如果当前ArrayList被修改(不仅有其他线程的事,也算上自己线程),这会导致当前ArrayList的数据与iterator创建时候的不一致(引起数据不一致,和数组越界)。
需要注意的是Object[] elementData = ArrayList.this.elementData;
,ArrayList的iterator每进行一次next()操作都会重新加载一次ArrayList数组,我觉得这是为了将iterator与其接口保持一致,还有减少iterator的内存空间,才这么设计的。
so如果将ArrayList数组保存在iterator里面,是不是就可以不用管当前iterator的数据与ArrayList的数据一致性了呢?这就引出了另外一种机制:fail-safe机制。
fail-safe机制
fail-safe机制是指集合被多个线程操作时,不会引起iterator中的数据改变。
在package java.util.concurrent;
中的集合类都是fail-safe的,比如CopyOnWriteArrayList
:
static final class COWIterator<E> implements ListIterator<E> {
/** Snapshot of the array */
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */
private int cursor;
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
}
CopyOnWriteArrayList的COWIterator在构造函数中将CopyOnWriteArrayList数组直接保存在final修饰的Object[]数组中,从而COWIterator中的数据一定不会被修改(final修饰的数组只能赋值一次)。
此外,也能看出Java的泛型真的是通过强制类型转换实现的(先变成Object,然后强制类型转换为<T>的类型)