fail-fast
fail-fast 當(dāng)有異郴氪耍或者錯誤發(fā)生時就立即中斷執(zhí)行滥沫。字面意思很抽象狐榔,其實就是java集合中的一種錯誤檢測機(jī)制,當(dāng)我們在遍歷集合元素的時候建椰,如果集合新增或刪除元素的話就會拋出異常雕欺,防止繼續(xù)遍歷。這就是所謂的快速失敗機(jī)制棉姐。
常見的集合操作:Hashmap, arrayList
1: 代碼演示
List<String> list = new ArrayList<>();
list.add("item1");
list.add("item2");
list.add("item3");
list.add("item4");
Iterator<String> it = list.listIterator();
int i = 0;
while (it.hasNext()) {
i++;
System.out.println(it.next());
if (2 == i) {
list.add("item5");
}
}
2: fail-fast機(jī)制是如何檢測
通過debug arrayList的源碼可以知道屠列,當(dāng)對arraylist做添加,刪除元素時候都會去修改內(nèi)部的一個成員變量modCount, 并且在遍歷arraylist的時候又會去檢查modcount和iterator產(chǎn)生的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<E> listIterator() {
return new ListItr(0);
}
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int expectedModCount = modCount;
public E next() {
checkForComodification();
...
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
...
}
在list.listIterator() 時就會創(chuàng)建出itr 同時設(shè)置exceptedModCount=modCount, 所以當(dāng)我們只循環(huán)中動態(tài)的添加了元素就會導(dǎo)致modCount 大于exceptedModCount, 從而拋出ConcurrentModificationException
fail-safe
fail-safe 和 fail-fast 是相對的笛洛,當(dāng)有異常或者錯誤發(fā)生時繼續(xù)執(zhí)行不會被中斷; 其實就是在對集合遍歷的時候乃坤,如果發(fā)生了新增苛让,刪除都會在一個復(fù)制的集合上進(jìn)行操作,不會拋出ConcurrentModificationException
常見的集合操作:CopyOnWriteArrayList, ConcurrentHashMap
1: 代碼演示
List<String> list = new CopyOnWriteArrayList<>();
list.add("item1");
list.add("item2");
list.add("item3");
list.add("item4");
Iterator<String> it = list.listIterator();
int i = 0;
while (it.hasNext()) {
i++;
System.out.println(it.next());
if (2 == i) {
list.add("item5");
}
}
2: fail-fast機(jī)制是如何保障的
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 生成一個新的數(shù)組
CopyOnWriteArrayList 迭代器的源碼
public ListIterator<E> listIterator() {
return new COWIterator<E>(getArray(), 0);
}
public ListIterator<E> listIterator(int index) {
Object[] elements = getArray();
int len = elements.length;
if (index < 0 || index > len)
throw new IndexOutOfBoundsException("Index: "+index);
return new COWIterator<E>(elements, index);
}
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;
}
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源碼可以看出來狱杰,其實每次迭代的時候都是訪問這個快照內(nèi)的元素,而不是原集合的元素(每次新增的時候都會做一次arrays.copyOf)
這種設(shè)計的好處是保證了在多線程操縱同一個集合的時候厅须,不會因為某個線程修改了集合仿畸,而影響其他正在迭代訪問集合的線程。缺點是,迭代器不能正確及時的反應(yīng)集合中的內(nèi)容错沽,而且一定程度上也增加了內(nèi)存的消耗簿晓。