快速失效與安全失效明棍,是針對(duì)迭代數(shù)據(jù)結(jié)構(gòu)過(guò)程出現(xiàn)的兩種說(shuō)法陪毡。
Iterator的安全失敗是基于對(duì)底層集合做拷貝,因此轮锥,它不受源集合上修改的影響矫钓。java.util包下面的所有的集合類(lèi)都是快速失敗的,而java.util.concurrent包下面的所有的類(lèi)都是安全失敗的舍杜⌒履龋快速失敗的迭代器會(huì)拋出ConcurrentModificationException異常,而安全失敗的迭代器永遠(yuǎn)不會(huì)拋出這樣的異常既绩。
fail-fast機(jī)制概龄,是一種錯(cuò)誤檢測(cè)機(jī)制。它只能被用來(lái)檢測(cè)錯(cuò)誤饲握,因?yàn)镴DK并不保證fail-fast機(jī)制一定會(huì)發(fā)生私杜。若在多線(xiàn)程環(huán)境下使用fail-fast機(jī)制的集合,建議使用“java.util.concurrent包下的類(lèi)”去取代“java.util包下的類(lèi)”救欧。
以ArraryList舉例衰粹,何時(shí)出現(xiàn)fail-fast事件,拋出ConcurrentModificationException異常笆怠。
public abstract class AbstractListextends AbstractCollectionimplements List {
...
// AbstractList中唯一的屬性
? ? // 用來(lái)記錄List修改的次數(shù):每修改一次(添加/刪除等操作)铝耻,將modCount+1
? ? protected transient int modCount =0;
? ? // 返回List對(duì)應(yīng)迭代器。實(shí)際上蹬刷,是返回Itr對(duì)象瓢捉。
? ? public Iteratoriterator() {
return new Itr();
? ? }
// Itr是Iterator(迭代器)的實(shí)現(xiàn)類(lèi)
? ? private class Itrimplements Iterator {
int cursor =0;
? ? ? ? int lastRet = -1;
? ? ? ? // 修改數(shù)的記錄值。
? ? ? ? // 每次新建Itr()對(duì)象時(shí)办成,都會(huì)保存新建該對(duì)象時(shí)對(duì)應(yīng)的modCount泡态;
? ? ? ? // 以后每次遍歷List中的元素的時(shí)候,都會(huì)比較expectedModCount和modCount是否相等迂卢;
? ? ? ? // 若不相等某弦,則拋出ConcurrentModificationException異常桐汤,產(chǎn)生fail-fast事件。
? ? ? ? int expectedModCount =modCount;
? ? ? ? public boolean hasNext() {
return cursor != size();
? ? ? ? }
public E next() {
// 獲取下一個(gè)元素之前刀崖,都會(huì)判斷“新建Itr對(duì)象時(shí)保存的modCount”和“當(dāng)前的modCount”是否相等惊科;
? ? ? ? ? ? // 若不相等拍摇,則拋出ConcurrentModificationException異常亮钦,產(chǎn)生fail-fast事件。
? ? ? ? ? ? checkForComodification();
? ? ? ? ? ? try {
E next = get(cursor);
? ? ? ? ? ? ? ? lastRet =cursor++;
? ? ? ? ? ? ? ? return next;
? ? ? ? ? ? }catch (IndexOutOfBoundsException e) {
checkForComodification();
? ? ? ? ? ? ? ? throw new NoSuchElementException();
? ? ? ? ? ? }
}
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
? ? ? ? ? ? checkForComodification();
? ? ? ? ? ? try {
AbstractList.this.remove(lastRet);
? ? ? ? ? ? ? ? if (lastRet
cursor--;
? ? ? ? ? ? ? ? lastRet = -1;
? ? ? ? ? ? ? ? expectedModCount =modCount;
? ? ? ? ? ? }catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
? ? ? ? ? ? }
}
final void checkForComodification() {
if (modCount !=expectedModCount)
throw new ConcurrentModificationException();
? ? ? ? }
}
...
}
在調(diào)用 next() 和 remove()時(shí)充活,都會(huì)執(zhí)行 checkForComodification()蜂莉。若 “modCount 不等于 expectedModCount”,則拋出ConcurrentModificationException異常混卵,產(chǎn)生fail-fast事件映穗。
在創(chuàng)建iterator的時(shí)候,modCount 與?expectedModCount 是相等的幕随,但是何時(shí)會(huì)被修改呢蚁滋?下面可以看一下ArraryList的源碼。
public class ArrayList?extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable {
...
// list中容量變化時(shí)赘淮,對(duì)應(yīng)的同步函數(shù)
? ? public void ensureCapacity(int minCapacity) {
modCount++;
? ? ? ? int oldCapacity = elementData.length;
? ? ? ? if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
? ? ? ? ? ? int newCapacity = (oldCapacity *3) /2 +1;
? ? ? ? ? ? if (newCapacity < minCapacity)
newCapacity = minCapacity;
? ? ? ? ? ? // minCapacity is usually close to size, so this is a win:
? ? ? ? ? ? elementData = Arrays.copyOf(elementData, newCapacity);
? ? ? ? }
}
// 添加元素到隊(duì)列最后
? ? public boolean add(E e) {
// 修改modCount
? ? ? ? ensureCapacity(size +1);? // Increments modCount!!
? ? ? ? elementData[size++] = e;
return true;
? ? }
// 添加元素到指定的位置
? ? public void add(int index, E element) {
if (index > size || index <0)
throw new IndexOutOfBoundsException(
"Index: " + index +", Size: " + size);
? ? ? ? // 修改modCount
? ? ? ? ensureCapacity(size +1);? // Increments modCount!!
? ? ? ? System.arraycopy(elementData, index, elementData, index +1,
? ? ? ? ? ? ? ? size - index);
? ? ? ? elementData[index] = element;
? ? ? ? size++;
? ? }
// 添加集合
? ? public boolean addAll(Collection c) {
Object[] a = c.toArray();
? ? ? ? int numNew = a.length;
? ? ? ? // 修改modCount
? ? ? ? ensureCapacity(size + numNew);? // Increments modCount
? ? ? ? System.arraycopy(a, 0, elementData, size, numNew);
? ? ? ? size += numNew;
? ? ? ? return numNew !=0;
? ? }
// 刪除指定位置的元素
? ? public E remove(int index) {
RangeCheck(index);
? ? ? ? // 修改modCount
? ? ? ? modCount++;
? ? ? ? E oldValue = (E) elementData[index];
? ? ? ? int numMoved = size - index -1;
? ? ? ? if (numMoved >0)
System.arraycopy(elementData, index +1, elementData, index, numMoved);
? ? ? ? elementData[--size] =null; // Let gc do its work
? ? ? ? return oldValue;
? ? }
// 快速刪除指定位置的元素
? ? private void fastRemove(int index) {
// 修改modCount
? ? ? ? modCount++;
? ? ? ? int numMoved = size - index -1;
? ? ? ? if (numMoved >0)
System.arraycopy(elementData, index +1, elementData, index,
? ? ? ? ? ? ? ? ? ? numMoved);
? ? ? ? elementData[--size] =null; // Let gc do its work
? ? }
// 清空集合
? ? public void clear() {
// 修改modCount
? ? ? ? modCount++;
? ? ? ? // Let gc do its work
? ? ? ? for (int i =0; i < size; i++)
elementData[i] =null;
? ? ? ? size =0;
? ? }
...
}
通過(guò)代碼我們可以看到:只要更改集合中的元素個(gè)數(shù):例如add(),remove(),clear()都會(huì)改變ModCount的值辕录。
接下來(lái),我們?cè)傧到y(tǒng)的梳理一下fail-fast是怎么產(chǎn)生的梢卸。步驟如下:
(01)?新建了一個(gè)ArrayList走诞,名稱(chēng)為arrayList。
(02)?向arrayList中添加內(nèi)容蛤高。
(03)新建一個(gè)“線(xiàn)程a”蚣旱,并在“線(xiàn)程a”中通過(guò)Iterator反復(fù)的讀取arrayList的值。
(04)?新建一個(gè)“線(xiàn)程b”戴陡,在“線(xiàn)程b”中刪除arrayList中的一個(gè)“節(jié)點(diǎn)A”塞绿。
(05) 之后,就會(huì)產(chǎn)生fail-fast事件了恤批,即:“線(xiàn)程a”創(chuàng)建了arrayList的Iterator位隶。此時(shí)“節(jié)點(diǎn)A”仍然存在于arrayList中,創(chuàng)建arrayList時(shí)开皿,expectedModCount = modCount(假設(shè)它們此時(shí)的值為N)涧黄。
? ? ? ?在“線(xiàn)程a”在遍歷arrayList過(guò)程中的某一時(shí)刻,“線(xiàn)程b”執(zhí)行了赋荆,并且“線(xiàn)程b”刪除了arrayList中的“節(jié)點(diǎn)A”笋妥。“線(xiàn)程b”執(zhí)行remove()進(jìn)行刪除操作時(shí)窄潭,在remove()中執(zhí)行了“modCount++”春宣,此時(shí)modCount變成了N+1!
“線(xiàn)程a”接著遍歷,當(dāng)它執(zhí)行到next()函數(shù)時(shí)月帝,調(diào)用checkForComodification()比較“expectedModCount”和“modCount”的大絮锿铩;而“expectedModCount=N”嚷辅,“modCount=N+1”,這樣簿姨,便拋出ConcurrentModificationException異常,產(chǎn)生fail-fast事件簸搞。
如何避免fail-fast問(wèn)題的產(chǎn)生呢扁位?java.util.current包幫你解決〕每。看一下CopyOnWriteArrayList
public class CopyOnWriteArrayList
implements List, RandomAccess, Cloneable, java.io.Serializable {
...
// 返回集合對(duì)應(yīng)的迭代器
? ? public Iteratoriterator() {
return new COWIterator(getArray(), 0);
? ? }
...
private static class COWIteratorimplements ListIterator {
private final Object[]snapshot;
? ? ? ? private int cursor;
? ? ? ? private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
? ? ? ? ? ? // 新建COWIterator時(shí)域仇,將集合中的元素保存到一個(gè)新的拷貝數(shù)組中。
? ? ? ? ? ? // 這樣寺擂,當(dāng)原始集合的數(shù)據(jù)改變暇务,拷貝數(shù)據(jù)中的值也不會(huì)變化。
? ? ? ? ? ? snapshot = elements;
? ? ? ? }
public boolean hasNext() {
return cursor
? ? ? ? }
public boolean hasPrevious() {
return cursor >0;
? ? ? ? }
public E next() {
if (!hasNext())
throw new NoSuchElementException();
? ? ? ? ? ? return (E)snapshot[cursor++];
? ? ? ? }
public E previous() {
if (!hasPrevious())
throw new NoSuchElementException();
? ? ? ? ? ? return (E)snapshot[--cursor];
? ? ? ? }
public int nextIndex() {
return cursor;
? ? ? ? }
public int previousIndex() {
return cursor -1;
? ? ? ? }
public void remove() {
throw new UnsupportedOperationException();
? ? ? ? }
public void set(E e) {
throw new UnsupportedOperationException();
? ? ? ? }
public void add(E e) {
throw new UnsupportedOperationException();
? ? ? ? }
}
...
}
(01) 和ArrayList繼承于A(yíng)bstractList不同怔软,CopyOnWriteArrayList沒(méi)有繼承于A(yíng)bstractList垦细,它僅僅只是實(shí)現(xiàn)了List接口。
(02) ArrayList的iterator()函數(shù)返回的Iterator是在A(yíng)bstractList中實(shí)現(xiàn)的爽雄;而CopyOnWriteArrayList是自己實(shí)現(xiàn)Iterator蝠检。
(03) ArrayList的Iterator實(shí)現(xiàn)類(lèi)中調(diào)用next()時(shí),會(huì)“調(diào)用checkForComodification()比較‘expectedModCount’和‘modCount’的大小”挚瘟;但是叹谁,CopyOnWriteArrayList的Iterator實(shí)現(xiàn)類(lèi)中,沒(méi)有所謂的checkForComodification()乘盖,更不會(huì)拋出ConcurrentModificationException異常焰檩。
個(gè)人公號(hào):【排骨肉段】,可以關(guān)注一下订框。