30分鐘掌握Fail-Fast機(jī)制 | 不信試一試

假如有兩個線程A與線程B的烁,線程A對集合C進(jìn)行迭代操作時(shí)诈闺,線程B改變了集合C的數(shù)據(jù)結(jié)構(gòu),此時(shí)會報(bào)出ConcurrentModificationException異常把曼,這就是Fail-Fast(快速失敗)漓穿,其為java中集合操作的快速檢錯機(jī)制。留下30分鐘叙赚,一看究竟僚饭。

一鳍鸵、Fail-Fast案例

public class FailFastTest {
    private static List<Integer> list = new ArrayList<Integer>();

    private static class ThreadOne extends Thread{
        public void run(){
            Iterator<Integer> it = list.iterator();
            while(it.hasNext()){
                Integer i = it.next();
                System.out.println("ThreadOne is using : "+ i);
                try {
                    Thread.sleep(5);
                } catch (Exception e) {
                    e.getStackTrace();
                }
            }
        }
    }
    
    private static class ThreadTwo extends Thread{
        public void run(){
            int i = 0;
            while(i < 6){
                if (i == 3 ) {
                    list.remove(i);
                }
                i++;
            }
        }
    }
    
    public static void main(String[] args) {
        for(int i=0 ; i < 10; i++ ){
            list.add(i);
        }
        new ThreadOne().start();
        new ThreadTwo().start();
    }
}

// 程序運(yùn)行后偿乖,報(bào)出異常,因?yàn)門hreadOne進(jìn)行迭代操作時(shí)媳禁,ThreadTwo對其進(jìn)行數(shù)據(jù)結(jié)構(gòu)的修改画切,程序報(bào)出異常提醒

二、源碼解讀

    // JDK1.8內(nèi)ArrayList中迭代器源碼
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount; // 由modCount決定毫别,值不會改變

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

// 在迭代器中的next()、remove()和forEachRemaining()方法中
// 均包含checkForComodification()钝计,而checkForComodification() // 方法
// 會拋出ConcurrentModificationException()異常私恬,
// 關(guān)鍵在于檢測modCount與expectedModCount是否相同
    //JDK1.8內(nèi)ArrayList中包含的方法,大都包含modCount++操作
    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }
  • 對ArrayList進(jìn)行迭代時(shí)疫衩,會不斷檢測expectedCount和modCount的值闷煤,如果其他操作修改了全局的modCount的值,則會報(bào)出異常鲤拿;

三、解決方案

  • 推薦使用CopyOnWriteArrayList近顷,先來解讀一下源碼:
    // JDK1.8內(nèi)CopyOnWriteArrayList中迭代器不進(jìn)行expectedCount和modCount檢查
    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++];
        }
    // JDK1.8內(nèi)CopyOnWriteArrayList中迭代器不進(jìn)行expectedCount和modCount檢查
    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

// add()方法生音,本質(zhì)是對于原數(shù)組的copy,不會存在修改集合數(shù)據(jù)結(jié)構(gòu)的操作窒升,其他方法類似

CopyOnWriteArrayList的核心原理:對于Array的操作(增刪改查)缀遍,都是基于復(fù)制到新copy數(shù)組,不會改變迭代器的對象饱须,修改完成后改變原有數(shù)據(jù)的引用即可域醇。

今天就暫時(shí)到這里啦!畢業(yè)季在匆忙與顧念中離去冤寿,回首一年前的畢業(yè)季歹苦,做好眼前事,珍惜校園時(shí)光殴瘦。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市号杠,隨后出現(xiàn)的幾起案子蚪腋,更是在濱河造成了極大的恐慌,老刑警劉巖姨蟋,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屉凯,死亡現(xiàn)場離奇詭異,居然都是意外死亡眼溶,警方通過查閱死者的電腦和手機(jī)悠砚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來堂飞,“玉大人灌旧,你說我怎么就攤上這事〈律福” “怎么了枢泰?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長铝噩。 經(jīng)常有香客問我衡蚂,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任毛甲,我火速辦了婚禮年叮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘丽啡。我一直安慰自己谋右,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布补箍。 她就那樣靜靜地躺著,像睡著了一般啸蜜。 火紅的嫁衣襯著肌膚如雪坑雅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天衬横,我揣著相機(jī)與錄音裹粤,去河邊找鬼。 笑死蜂林,一個胖子當(dāng)著我的面吹牛遥诉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播噪叙,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼矮锈,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了睁蕾?” 一聲冷哼從身側(cè)響起苞笨,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎子眶,沒想到半個月后瀑凝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡臭杰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年粤咪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片渴杆。...
    茶點(diǎn)故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡寥枝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出将塑,到底是詐尸還是另有隱情脉顿,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布点寥,位于F島的核電站艾疟,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蔽莱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一弟疆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盗冷,春花似錦怠苔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锅劝,卻和暖如春攒驰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背故爵。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工玻粪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人诬垂。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓劲室,卻偏偏與公主長得像,于是被迫代替她去往敵國和親结窘。 傳聞我的和親對象是個殘疾皇子很洋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評論 2 348

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

  • 快速失效與安全失效,是針對迭代數(shù)據(jù)結(jié)構(gòu)過程出現(xiàn)的兩種說法晦鞋。 Iterator的安全失敗是基于對底層集合做拷貝蹲缠,因此...
    樂百事52淑熙閱讀 2,255評論 0 0
  • 概要 前面,我們已經(jīng)學(xué)習(xí)了ArrayList悠垛。接下來线定,我們以ArrayList為例,對Iterator的fail-...
    程序員歐陽閱讀 512評論 1 1
  • 《開動大腦》一書确买,是托尼·巴贊早期寫的書斤讥,在當(dāng)時(shí)所提到理念非常超前,獲得很大的反響湾趾。它的第一版是1974年春天發(fā)行...
    MousseSs閱讀 216評論 0 1
  • 很多時(shí)候我們都認(rèn)為搀缠,心理咨詢都是讓人變得更開心铛楣,但是其實(shí)也會讓人去直面悲傷,因?yàn)椴幻鎸Ρ瘋目鞓范际撬A髅ァ?有一...
    費(fèi)漠塵閱讀 1,045評論 5 17
  • 訪談目的不夠明確R掌铡tぶ荨鉴竭! 訪談目的不夠明確!0痘搿搏存! 訪談目的不夠明確!J钢蕖璧眠! 最重要的說3遍!訪談情況瞬息萬變读虏,可能一不...
    i蠟克閱讀 1,605評論 0 0