fail-fast
fail-fast 当有异常或者错误发生时就立即中断执行。字面意思很抽象,其实就是java集合中的一种错误检测机制,当我们在遍历集合元素的时候,如果集合新增或删除元素的话就会抛出异常,防止继续遍历。这就是所谓的快速失败机制。
常见的集合操作:Hashmap, arrayList
1: 代码演示
List list = new ArrayList<>();
list.add("item1");
list.add("item2");
list.add("item3");
list.add("item4");
Iterator it = list.listIterator();
int i = 0;
while (it.hasNext()) {
i++;
System.out.println(it.next());
if (2 == i) {
list.add("item5");
}
}
2: fail-fast机制是如何检测
通过debug arrayList的源码可以知道,当对arraylist做添加,删除元素时候都会去修改内部的一个成员变量modCount, 并且在遍历arraylist的时候又会去检查modcount和iterator产生的expectedModCount是否相等,不相等则抛出ConcurrentModificationException异常
ArrayList Add方法源码:
protected transient int modCount = 0;
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 会修改 modCount
....
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
ArrayList 迭代器的源码
public ListIterator listIterator() {
return new ListItr(0);
}
public Iterator iterator() {
return new Itr();
}
private class Itr implements Iterator {
int expectedModCount = modCount;
public E next() {
checkForComodification();
...
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
...
}
在list.listIterator() 时就会创建出itr 同时设置exceptedModCount=modCount, 所以当我们只循环中动态的添加了元素就会导致modCount 大于exceptedModCount, 从而抛出ConcurrentModificationException
fail-safe
fail-safe 和 fail-fast 是相对的,当有异常或者错误发生时继续执行不会被中断; 其实就是在对集合遍历的时候,如果发生了新增,删除都会在一个复制的集合上进行操作,不会抛出ConcurrentModificationException
常见的集合操作:CopyOnWriteArrayList, ConcurrentHashMap
1: 代码演示
List list = new CopyOnWriteArrayList<>();
list.add("item1");
list.add("item2");
list.add("item3");
list.add("item4");
Iterator it = list.listIterator();
int i = 0;
while (it.hasNext()) {
i++;
System.out.println(it.next());
if (2 == i) {
list.add("item5");
}
}
2: fail-fast机制是如何保障的
CopyOnWriteArrayList add 方法源码
public void add(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
Object[] newElements;
int numMoved = len - index;
if (numMoved == 0)
newElements = Arrays.copyOf(elements, len + 1);
else {
newElements = new Object[len + 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
newElements[index] = element;
setArray(newElements);
} finally {
lock.unlock();
}
}
从源码中可以看出来,每次添加一个新的元素都会做一次arrays.copyOf 生成一个新的数组
CopyOnWriteArrayList 迭代器的源码
public ListIterator listIterator() {
return new COWIterator(getArray(), 0);
}
public ListIterator listIterator(int index) {
Object[] elements = getArray();
int len = elements.length;
if (index < 0 || index > len)
throw new IndexOutOfBoundsException("Index: "+index);
return new COWIterator(elements, index);
}
static final class COWIterator implements ListIterator {
/** 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;
}
public boolean hasNext() {
return cursor < snapshot.length;
}
public boolean hasPrevious() {
return cursor > 0;
}
@SuppressWarnings("unchecked")
public E next() {
if (! hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
}
………..
}
从COWIterator源码可以看出来,其实每次迭代的时候都是访问这个快照内的元素,而不是原集合的元素(每次新增的时候都会做一次arrays.copyOf)
这种设计的好处是保证了在多线程操纵同一个集合的时候,不会因为某个线程修改了集合,而影响其他正在迭代访问集合的线程。缺点是,迭代器不能正确及时的反应集合中的内容,而且一定程度上也增加了内存的消耗。