fail-fast 與 fail-safe 機(jī)制有什么區(qū)別

快速失效與安全失效明棍,是針對(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)注一下订框。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末析苫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子穿扳,更是在濱河造成了極大的恐慌衩侥,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件矛物,死亡現(xiàn)場(chǎng)離奇詭異茫死,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)履羞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)峦萎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)屡久,“玉大人,你說(shuō)我怎么就攤上這事爱榔”换罚” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵详幽,是天一觀(guān)的道長(zhǎng)筛欢。 經(jīng)常有香客問(wèn)我,道長(zhǎng)妒潭,這世上最難降的妖魔是什么悴能? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任揣钦,我火速辦了婚禮雳灾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘冯凹。我一直安慰自己谎亩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布宇姚。 她就那樣靜靜地躺著匈庭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪浑劳。 梳的紋絲不亂的頭發(fā)上阱持,一...
    開(kāi)封第一講書(shū)人閱讀 49,760評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音魔熏,去河邊找鬼衷咽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蒜绽,可吹牛的內(nèi)容都是我干的镶骗。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼躲雅,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鼎姊!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起相赁,我...
    開(kāi)封第一講書(shū)人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤相寇,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后钮科,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體唤衫,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年跺嗽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了战授。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片页藻。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖植兰,靈堂內(nèi)的尸體忽然破棺而出份帐,到底是詐尸還是另有隱情,我是刑警寧澤楣导,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布废境,位于F島的核電站,受9級(jí)特大地震影響筒繁,放射性物質(zhì)發(fā)生泄漏噩凹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一毡咏、第九天 我趴在偏房一處隱蔽的房頂上張望驮宴。 院中可真熱鬧,春花似錦呕缭、人聲如沸堵泽。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)迎罗。三九已至,卻和暖如春片仿,著一層夾襖步出監(jiān)牢的瞬間纹安,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工砂豌, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留厢岂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓奸鸯,卻偏偏與公主長(zhǎng)得像咪笑,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子娄涩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容