Java集合(三)--fail-fast機制

上一篇文章我們分析了一些ArrayList的簡單的源碼,在分析的過程中,我們發(fā)現(xiàn)在調(diào)用add()、remove()和clear()及其同類方法時,ArrayList的modCount屬性都要加1副女,調(diào)用clone()方法時,新的數(shù)組的modCount屬性要置為0蚣旱。這里就很奇怪了碑幅,這個modCount是什么戴陡?他參與了哪些與ArrayList有關的操作?把他加1的意義是什么沟涨?帶著這些問題恤批,我們開始本篇文章的內(nèi)容。

從ArrayList中的modCount入手

要知道一個屬性的作用裹赴,就要找到它是在哪里被聲明的喜庞。點擊ArrayList的modCout,我們會進入到AbstractList中棋返。這里延都,就是modCount屬性被聲明的地方。ArrayList中的modCount是其繼承的AbstractList的一個屬性睛竣。

在AbstractList中對modCount的解釋為:modCount表示此列表結(jié)構(gòu)已被修改的次數(shù)晰房。 結(jié)構(gòu)修改是指一些改變列表大小或以其他方式擾亂它的方式,會導致正在進行的迭代可能產(chǎn)生不正確的結(jié)果射沟。

正在進行的迭代殊者?通過集合的第一篇文章,我們知道ArrayList向上追溯是實現(xiàn)了Collection接口的验夯,而Collection接口又繼承了Iterable接口來實現(xiàn)對集合中元素的遍歷幽污。在Iterable接口中,獲取迭代器的方法是iterator()方法簿姨。那我們看一下ArrayList所實現(xiàn)的iterator()方法方法做了些什么

public Iterator<E> iterator() {
        return new Itr();
    }

我們看到它返回了一個Itr的實例。這個Itr又是什么簸搞,我們繼續(xù)看下去:

//此處將ArrayList的modCount也放進來方便分析
protected transient int modCount = 0;

//Itr是Iterator接口的一個實現(xiàn)類
private class Itr implements Iterator<E> {

    //獲取ArrayList的數(shù)組大小
    protected int limit = ArrayList.this.size;

        //下一個元素的索引
         int cursor; 
        //上一次遍歷過的元素的索引; 如果沒有的話返回-1   
         int lastRet = -1; 
        //將初始值置為modCount(此屬性很重要扁位,記住它,往下看)
         int expectedModCount = modCount; 

        //判斷是否有下一個元素
        public boolean hasNext() {
            return cursor < limit;
        }

        //獲取下一個元素
        @SuppressWarnings("unchecked")
        public E next() {
            //在獲取下一個元素之前趁俊,判斷當前的modCount與expectedModCount是否相等域仇,不等則拋出異常
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            int i = cursor;
            if (i >= limit)
                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();
            //在刪除元素之前,判斷當前的modCount與expectedModCount是否相等寺擂,不等則拋出異常
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
                limit--;
            } 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;
            //判斷下一個元素的索引是否大于數(shù)組的長度暇务,是則拋出異常
            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;

            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

}

在上面的源碼中,我們注意Itr有三個屬性:cursor怔软、lastRet和expectedModCount垦细。其中,expectedModCount就是我們分析問題的關鍵挡逼。我們發(fā)現(xiàn)expectedModCount在創(chuàng)建Itr實例的時候就會賦初值括改,值為創(chuàng)建Itr實例時的modCount,在Itr創(chuàng)建完成后expectedModCount的值就不再變化了家坎,而modCount呢嘱能?每當ArrayList調(diào)用add()吝梅、remove()和clear()及其相關方法的時候都會加1。所以惹骂,當我們創(chuàng)建了一個Itr實例itr苏携,然后修改了ArrayList結(jié)構(gòu),則使用itr遍歷這個List就會報錯对粪。因為此時expectedModCount 右冻!= modCount。現(xiàn)在想想衩侥,為什么要有這個判斷呢国旷?它預防或者解決了什么問題?

現(xiàn)在茫死,進入本篇文章的主題跪但。

fail-fast機制

fail-fast機制是Java集合中的一個錯誤機制。在創(chuàng)建迭代器之后的任何時候峦萎,除了通過迭代器自己的remove()或add()方法之外屡久,其他導致列表在結(jié)構(gòu)上被修改的行為,都會導致則此類的iterator()和listIterator()方法返回的迭代器fail-fast(快速失敗)爱榔。迭代器將拋出ConcurrentModificationException異常被环。比如,我們看一個簡單的例子:

List<String> list = new ArrayList<>();
        list.add("213");
        list.add("213");
        list.add("213");
        Iterator<String> iterator = list.iterator();
        list.add("asd");
        iterator.next();

本例運行到iterator.next()就會拋出一個ConcurrentModificationException異常详幽,因為我們創(chuàng)建了list的迭代器之后筛欢,又對list的結(jié)構(gòu)做了修改。通過上面的分析唇聘,我們知道此時expectedModCount 版姑!= modCount,所以拋出了異常迟郎。同樣的情況還有多線程操作同一個ArrayList剥险。

現(xiàn)在,我們知道了fail-fast出現(xiàn)的原因和原理宪肖,也就知道了該怎么預防這種情況表制,比如:在多線程的情況下,使用線程安全的集合類來保證線程安全控乾;創(chuàng)建了一個List的Iterator實例后么介,在其完成遍歷之前,不要使用List的add()阱持、remove()和clear()及其相關方法等等夭拌。

好了,本文就分析到這,ending...

(馬上元旦了鸽扁,祝大家(如果有人看的話)也祝自己新的一年有更大的進步K庹馈!桶现!生而有崖躲雅,學無止境)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市骡和,隨后出現(xiàn)的幾起案子相赁,更是在濱河造成了極大的恐慌,老刑警劉巖慰于,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钮科,死亡現(xiàn)場離奇詭異,居然都是意外死亡婆赠,警方通過查閱死者的電腦和手機绵脯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來休里,“玉大人蛆挫,你說我怎么就攤上這事∶钍颍” “怎么了悴侵?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拭嫁。 經(jīng)常有香客問我可免,道長,這世上最難降的妖魔是什么做粤? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任巴元,我火速辦了婚禮,結(jié)果婚禮上驮宴,老公的妹妹穿的比我還像新娘。我一直安慰自己呕缭,他們只是感情好堵泽,可當我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著恢总,像睡著了一般迎罗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上片仿,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天纹安,我揣著相機與錄音,去河邊找鬼。 笑死厢岂,一個胖子當著我的面吹牛光督,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播塔粒,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼结借,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了卒茬?” 一聲冷哼從身側(cè)響起船老,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎圃酵,沒想到半個月后柳畔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡郭赐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年薪韩,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片堪置。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡躬存,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出舀锨,到底是詐尸還是另有隱情岭洲,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布坎匿,位于F島的核電站盾剩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏替蔬。R本人自食惡果不足惜告私,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望承桥。 院中可真熱鬧驻粟,春花似錦、人聲如沸凶异。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽剩彬。三九已至酷麦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間喉恋,已是汗流浹背沃饶。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工母廷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人糊肤。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓琴昆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親轩褐。 傳聞我的和親對象是個殘疾皇子椎咧,可洞房花燭夜當晚...
    茶點故事閱讀 43,554評論 2 349

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