Java fail-fast 和 fail-safe机制
Java fail-fast 和 fail-safe机制
基本概念
fail-fast 和 fail-safe这两个概念都是和集合的并发修改相关的。集合的并发修改是指:当一个线程正在遍历集合时,当另外 一个线程(或线程本身)修改了集合。
fail-fast
fail-fast 机制是指在遍历集合的过程中如果发现了集合被改变,则抛出ConcurrentModificationException。通常有如下两种情况:
- 遍历集合过程中本修改了集合
- 多线程的情况下,本线程遍历集合过程中,其他线程修改了集合。
fail-fast的检测
以ArrayList代码为例,从代码可以看出expectedModCount初始化为modCount, 其中modeCount是ArrayList被修改(add remove)的次数,iterator next 以及remove接口 都会检查expectedModCount和modCount是否相等,如果不等则抛出异常。
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
protected int limit = ArrayList.this.size;
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 < limit;
}
@SuppressWarnings("unchecked")
public E next() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
一个例子:
ArrayList<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
for (String str : list) {
list.add("D") ;
}
输出结果:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at TestJava.main(TestJava.java:14)
这里隐含了一个知识点,对于foreach循环,javac编译器实际上是将其处理成iterator迭代。
fail-safe
fail-safe 机制是指在遍历集合的过程中如果发现了集合被改变,遍历集合依然可以正常进行,以CopyOnWriteArrayList为例:
public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
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;
COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
//...
}
public boolean add(E e) {
synchronized (lock) {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
}
}
public E remove(int index) {
synchronized (lock) {
Object[] elements = getArray();
int len = elements.length;
E oldValue = get(elements, index);
int numMoved = len - index - 1;
if (numMoved == 0)
setArray(Arrays.copyOf(elements, len - 1));
else {
Object[] newElements = new Object[len - 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
setArray(newElements);
}
return oldValue;
}
}
final void setArray(Object[] a) {
elements = a;
}
从代码中可以看到:iterator返回的是COWIterator,COWIterator实际上遍历的是,当前的elements。而在每次都会将旧的元素拷贝到一个新的数组中然后再进行操作.
一个例子
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
for (String str : list) {
System.out.print(str + "\r\n");
}
输出结果为:
A
B
C