fail-fast and fail-safe

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");
    }
}
image.png

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");
    }
}
image.png

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)存的消耗簿晓。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市甥捺,隨后出現(xiàn)的幾起案子抢蚀,更是在濱河造成了極大的恐慌,老刑警劉巖镰禾,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皿曲,死亡現(xiàn)場離奇詭異,居然都是意外死亡吴侦,警方通過查閱死者的電腦和手機(jī)屋休,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來备韧,“玉大人劫樟,你說我怎么就攤上這事≈茫” “怎么了叠艳?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長易阳。 經(jīng)常有香客問我附较,道長,這世上最難降的妖魔是什么潦俺? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任拒课,我火速辦了婚禮,結(jié)果婚禮上事示,老公的妹妹穿的比我還像新娘早像。我一直安慰自己,他們只是感情好肖爵,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布卢鹦。 她就那樣靜靜地躺著,像睡著了一般劝堪。 火紅的嫁衣襯著肌膚如雪法挨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天幅聘,我揣著相機(jī)與錄音凡纳,去河邊找鬼。 笑死帝蒿,一個胖子當(dāng)著我的面吹牛荐糜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼暴氏,長吁一口氣:“原來是場噩夢啊……” “哼延塑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起答渔,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤关带,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后沼撕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宋雏,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年务豺,在試婚紗的時候發(fā)現(xiàn)自己被綠了磨总。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡笼沥,死狀恐怖蚪燕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情奔浅,我是刑警寧澤馆纳,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站汹桦,受9級特大地震影響鲁驶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜营勤,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一灵嫌、第九天 我趴在偏房一處隱蔽的房頂上張望壹罚。 院中可真熱鬧葛作,春花似錦、人聲如沸猖凛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辨泳。三九已至虱岂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間菠红,已是汗流浹背第岖。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留试溯,地道東北人蔑滓。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親键袱。 傳聞我的和親對象是個殘疾皇子燎窘,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

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

  • 快速失效與安全失效,是針對迭代數(shù)據(jù)結(jié)構(gòu)過程出現(xiàn)的兩種說法蹄咖。 Iterator的安全失敗是基于對底層集合做拷貝褐健,因此...
    樂百事52淑熙閱讀 2,267評論 0 0
  • 背景 設(shè)想一個場景,我們需要將一個集合中滿足條件的元素刪除:客戶端提交了一個Array類型的數(shù)據(jù)澜汤,經(jīng)過Spring...
    XHLeee閱讀 299評論 0 1
  • 一银亲、Fail-Fast慢叨、Fail-Safe系統(tǒng)簡介 Fail-Fast系統(tǒng)好,還是Fail-Safe系統(tǒng)好务蝠,這始終...
    hei禹閱讀 557評論 0 1
  • 什么是并發(fā)修改 當(dāng)一個或多個線程正在遍歷一個集合(Collection)拍谐,此時另一個線程修改了這個集合(添加,刪除...
    markeNick閱讀 420評論 0 1
  • Java fail-fast 和 fail-safe機(jī)制 基本概念 fail-fast 和 fail-safe這兩...
    ObadiObada閱讀 457評論 0 3