Java ConcurrentModificationException 解析

概述

ConcurrentModificationException 可以直接從字面理解:同時(shí)更改異常渡讼。也就是說城菊,在對(duì)某一數(shù)據(jù)執(zhí)行某一操作的時(shí)候,同時(shí)更改了數(shù)據(jù)讹躯,造成錯(cuò)誤。

舉個(gè)栗子

在遍歷一個(gè)List同時(shí)remove其中的某個(gè)item

package com.example;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class MyClass {
    static List<String> mList = new ArrayList<>();

    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        myClass.initData();
        //forEach遍歷同時(shí)remove某個(gè)item
        for (String s : mList) {
            if ("aaa".equals(s)) {
                mList.remove(s);
            }
        }
        myClass.iterator(mList);
        //模擬forEach遍歷同時(shí)remove某個(gè)item
        Iterator<String> iterator = mList.iterator();
        while (iterator.hasNext()) {
            String s = iterator.next();
            if ("aaa".equals(s)) {
                mList.remove(s);
            }
        }
        myClass.iterator(mList);
        //iterator遍歷同時(shí)remove某個(gè)item
        Iterator<String> iterator1 = mList.iterator();
        while (iterator1.hasNext()) {
            String s = iterator1.next();
            if ("aaa".equals(s)) {
                iterator1.remove();
            }
        }
        myClass.iterator(mList);
        //for遍歷同時(shí)remove某個(gè)item
        for (int i = 0; i < mList.size(); i++) {
            String s = mList.get(i);
            if ("aaa".equals(s)) {
                mList.remove(s);
            }
        }
        myClass.iterator(mList);
    }

    public void initData() {
        mList.add("aaa");
        mList.add("bbb");
        mList.add("ccc");
        mList.add("ddd");
    }

    public void iterator(List<String> list) {
        for (String s : list) {
            System.out.println(s+"  ");
        }
    }
}

分別用這四種實(shí)現(xiàn)邏輯去遍歷(注釋其它的三種實(shí)現(xiàn))缠劝,打印結(jié)果依次是:

image.png
image.png
image.png
image.png

分析問題

從打印的結(jié)果我們可以看到前面兩種實(shí)現(xiàn)會(huì)報(bào)ConcurrentModificationException 潮梯,其實(shí)這兩種實(shí)現(xiàn)本質(zhì)是一種,第一種forEach的方式其實(shí)是在第二種iterator上包了一層語(yǔ)法糖惨恭。第三種實(shí)現(xiàn)是iterator的正確實(shí)現(xiàn)方式秉馏,對(duì)比一下第二種,區(qū)別只是在mList.remove和iterator.remove喉恋,OK沃饶,這樣就成功的縮小了問題范圍。接下來轻黑,直接看源碼:

/**
     * Returns an iterator over the elements in this list in proper sequence.
     *
     * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
     *
     * @return an iterator over the elements in this list in proper sequence
     */
    public Iterator<E> iterator() {
        return new Itr();
    }

    /**
     * An optimized version of AbstractList.Itr
     */
    private class Itr implements Iterator<E> {    
        @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();
            }
        }
    }

重點(diǎn)看remove方法糊肤,ArrayList.this.remove(lastRet)之后,有一個(gè)expectedModCount = modCount氓鄙。再看next()方法的checkForComodification():

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

問題就出在這里馆揉,iterator的remove方法有一個(gè)同步計(jì)數(shù)的邏輯。

總結(jié)

在遍歷List的時(shí)候抖拦,如果對(duì)List數(shù)據(jù)有改動(dòng)升酣,不應(yīng)用forEach的遍歷方式,可以用for或者iterator來替代态罪。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末噩茄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子复颈,更是在濱河造成了極大的恐慌绩聘,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異凿菩,居然都是意外死亡机杜,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門衅谷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來椒拗,“玉大人,你說我怎么就攤上這事获黔∈纯粒” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵玷氏,是天一觀的道長(zhǎng)枉阵。 經(jīng)常有香客問我,道長(zhǎng)预茄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任侦厚,我火速辦了婚禮耻陕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘刨沦。我一直安慰自己诗宣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布想诅。 她就那樣靜靜地躺著召庞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪来破。 梳的紋絲不亂的頭發(fā)上篮灼,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音徘禁,去河邊找鬼诅诱。 笑死,一個(gè)胖子當(dāng)著我的面吹牛送朱,可吹牛的內(nèi)容都是我干的娘荡。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼驶沼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼炮沐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起回怜,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤大年,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鲜戒,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡专控,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了遏餐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伦腐。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖失都,靈堂內(nèi)的尸體忽然破棺而出柏蘑,到底是詐尸還是另有隱情,我是刑警寧澤粹庞,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布咳焚,位于F島的核電站,受9級(jí)特大地震影響庞溜,放射性物質(zhì)發(fā)生泄漏革半。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一流码、第九天 我趴在偏房一處隱蔽的房頂上張望又官。 院中可真熱鬧,春花似錦漫试、人聲如沸六敬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)外构。三九已至,卻和暖如春播掷,著一層夾襖步出監(jiān)牢的瞬間审编,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工叮趴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留割笙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓眯亦,卻偏偏與公主長(zhǎng)得像伤溉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子妻率,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

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

  • 傳送門 解讀阿里Java開發(fā)手冊(cè)(v1.1.1) - 異常日志 前言 阿里Java開發(fā)手冊(cè)談不上圣經(jīng)乱顾,但確實(shí)是大量...
    kelgon閱讀 4,350評(píng)論 4 50
  • java筆記第一天 == 和 equals ==比較的比較的是兩個(gè)變量的值是否相等,對(duì)于引用型變量表示的是兩個(gè)變量...
    jmychou閱讀 1,488評(píng)論 0 3
  • Java源碼研究之容器(1) 如何看源碼 很多時(shí)候我們看源碼, 看完了以后經(jīng)常也沒啥收獲, 有些地方看得懂, 有些...
    駱駝騎士閱讀 987評(píng)論 0 22
  • 愛你是孤單的心事券时,是我想藏在心里不說又想說的爭(zhēng)執(zhí),是我想說卻又不敢說心思伏伯,是我想忘卻又忘不掉的堅(jiān)持橘洞,是我小心翼翼卻...
    三之道閱讀 948評(píng)論 0 1
  • 今天我從學(xué)校東門走到了北門,很奇怪吧说搅!不就是從東門走到了北門嗎炸枣?有什么了不起的。不弄唧,不是了不起适肠。是我終于一個(gè)...
    久不煩閱讀 593評(píng)論 0 0