最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

關(guān)于集合中的并發(fā)修改異常及處理方式

 更新時(shí)間:2025年06月30日 10:09:36   作者:找不到、了  
這篇文章主要介紹了關(guān)于集合中的并發(fā)修改異常及處理方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

前言

關(guān)于集合的總結(jié),可參考如下圖:

在Java中,像ArrayList這樣的集合類使用迭代器的時(shí)候,如果在遍歷過程中直接修改集合(比如remove),可能會(huì)導(dǎo)致ConcurrentModificationException。

為什么是可能會(huì)產(chǎn)生導(dǎo)致ConcurrentModificationException?

因?yàn)樵谛薷募系慕Y(jié)構(gòu)化修改modeCount的過程中,如果修改了集合的索引(索引前移),則不會(huì)發(fā)生異常。

1、并發(fā)異常介紹

ConcurrentModificationException的核心原因是:迭代器檢測到集合在遍歷過程中被修改,導(dǎo)致狀態(tài)不一致。

1.1、for-each的本質(zhì)

在 Java 中,如果你在for-each循環(huán)(即增強(qiáng)型 for 循環(huán))中直接對集合執(zhí)行remove()操作,會(huì)拋出ConcurrentModificationException。

這是因?yàn)閒or-each循環(huán)底層依賴于迭代器(Iterator)來遍歷集合,而迭代器在設(shè)計(jì)時(shí)為了保證遍歷的一致性和安全性,對集合的結(jié)構(gòu)修改有嚴(yán)格的限制。

如下所示,for-each的本質(zhì):

for (Element e : collection) {
    // do something
}

等價(jià)于:
Iterator<Element> it = collection.iterator();
while (it.hasNext()) {
    Element e = it.next();
    // do something
}

1.2、調(diào)用list.remove()的后果

在循環(huán)中直接調(diào)用list.remove()(element)或list.remove()(index),會(huì)直接修改集合的結(jié)構(gòu),導(dǎo)致modCount自動(dòng)遞增。

此時(shí)迭代器的expectedModCount并未更新,因此在下一次調(diào)用it.next()時(shí),會(huì)檢測到不一致并拋出異常。

2、迭代器

2.1、設(shè)計(jì)原理

Java 集合框架中的迭代器(Iterator)在設(shè)計(jì)時(shí)引入了一種并發(fā)修改檢查機(jī)制,用于在遍歷過程中檢測集合的結(jié)構(gòu)是否被外部修改。

機(jī)制的核心目的保證迭代過程中集合的一致性,防止因并發(fā)修改導(dǎo)致的不可預(yù)期行為。

1、快速失?。‵ail-Fast)策略

Java 集合的迭代器采用快速失敗策略:一旦檢測到并發(fā)修改,立即拋出異常,防止后續(xù)操作產(chǎn)生不可預(yù)知的結(jié)果??焖偈〔荒鼙WC 100% 檢測到所有并發(fā)修改,但能顯著降低錯(cuò)誤概率。

2、安全性與性能的權(quán)衡

并發(fā)檢查機(jī)制增加了少量性能開銷,但保障了迭代過程的安全性。對于高并發(fā)場景,可選擇線程安全的集合類(如ConcurrentHashMap)。

2.2、并發(fā)檢查機(jī)制的屬性

1.modCountexpectedModCount

modCount:

  • 是集合類(如ArrayList、HashMap)中的一個(gè)字段,表示集合的結(jié)構(gòu)性修改次數(shù)(如添加、刪除元素)。
  • 每次對集合進(jìn)行結(jié)構(gòu)性修改(如add()、remove()),modCount會(huì)自動(dòng)遞增。

expectedModCount:

是迭代器內(nèi)部保存的一個(gè)字段,表示迭代器創(chuàng)建時(shí)集合的modCount值。

  • 在調(diào)用it.next()時(shí),迭代器內(nèi)部會(huì)檢查當(dāng)前集合的modCount是否與迭代器創(chuàng)建時(shí)記錄的expectedModCount一致。
  • 如果不一致,就會(huì)拋出ConcurrentModificationException。

2.檢查邏輯

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

如果集合的modCount被修改(即modCount != expectedModCount),迭代器會(huì)拋出ConcurrentModificationException,表示檢測到并發(fā)修改。

2.3、機(jī)制的工作流程

1、迭代器創(chuàng)建時(shí)

迭代器在創(chuàng)建時(shí)會(huì)記錄當(dāng)前集合的modCount值,并將其賦值給expectedModCount。

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

private class Itr implements Iterator<E> {
    int expectedModCount = modCount; // 記錄初始狀態(tài)
    ...
}

2、迭代過程中調(diào)用next()

每次調(diào)用next()方法時(shí),迭代器會(huì)檢查modCount和expectedModCount是否一致。

public E next() {
    checkForComodification(); // 檢查是否被修改
    ...
}

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

3、調(diào)用iterator.remove()

如果通過迭代器的remove()方法刪除集合的元素,此時(shí)迭代器iterator會(huì)同步更新modCount和expectedModCount,確保一致性。

public void remove() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    // 刪除元素并更新 modCount
    ArrayList.this.remove(size - 1);
    cursor--;
    expectedModCount = modCount; // 同步更新
}

??注意:如果直接使用list.remove(),modCount會(huì)增加,下次再調(diào)用的時(shí)候,會(huì)拋異常。

2.4、適用場景

1.單線程下的結(jié)構(gòu)修改檢測

該機(jī)制不僅適用于多線程環(huán)境,也適用于單線程中在迭代過程中直接修改集合的情況。

2.多線程下的并發(fā)修改

在多線程環(huán)境下,如果多個(gè)線程同時(shí)修改集合的元素,也可能導(dǎo)致modCount與expectedModCount不一致。

此時(shí),迭代器的并發(fā)檢查機(jī)制可以檢測到這種沖突,但并不能完全解決線程安全問題。需要結(jié)合線程安全集合(如CopyOnWriteArrayList)或同步機(jī)制。

3、解決方案

3.1、迭代器的remove()

Iterator<Element> it = list.iterator();
while (it.hasNext()) {
    Element e = it.next();
    if (someCondition(e)) {
        it.remove(); // 安全地刪除元素
    }
}

it.remove()是迭代器提供的方法,它會(huì)同步更新modCount和expectedModCount,避免異常。

3.2、普通for循環(huán) + 控制索引

for (int i = 0; i < list.size(); i++) {
    Element e = list.get(i);
    if (someCondition(e)) {
        list.remove(i);
        i--; // 刪除后索引前移
    }
}
  • 注意:刪除元素后需要調(diào)整索引,防止跳過元素。

3.3、CopyOnWriteArrayList

如果確實(shí)需要在遍歷過程中頻繁修改集合,可以使用線程安全的CopyOnWriteArrayList:

List<Element> list = new CopyOnWriteArrayList<>();
for (Element e : list) {
    if (someCondition(e)) {
        list.remove(e); // 不會(huì)拋出異常
    }
}
  • 該集合在修改時(shí)會(huì)復(fù)制底層數(shù)組,避免直接修改共享數(shù)據(jù),但性能開銷較大。

3.4、Collections.synchronizedlist(list)

在多線程環(huán)境中使用Collections.synchronizedList時(shí),遍歷(Iteration)并修改列表的過程中,必須手動(dòng)加鎖,以確保線程安全。

這是因?yàn)樵谀J(rèn)情況下,Collections.synchronizedList的同步機(jī)制僅覆蓋其方法調(diào)用,但不包括迭代器(Iterator)的線程安全性。

1、為什么需要手動(dòng)加鎖?

  • Collections.synchronizedList的同步機(jī)制
  • Collections.synchronizedList返回的列表是一個(gè)線程安全的包裝類,其所有方法(如add、remove、get等)都通過synchronized關(guān)鍵字加鎖,確保單個(gè)方法調(diào)用的線程安全

2.迭代器(Iterator)的線程安全性缺失

  • 雖然列表的方法是線程安全的,但迭代器本身并不是線程安全的。
  • 例如:如果線程 A 正在遍歷列表(使用iterator()),而線程 B 同時(shí)修改了列表(如add或remove),即使這些方法是同步的,迭代器也可能拋出ConcurrentModficationException,或者看到不一致的數(shù)據(jù)狀態(tài)。

代碼示例如下:

List<String> list = Collections.synchronizedList(new ArrayList<>());

// 添加元素
list.add("A");
list.add("B");

// 遍歷時(shí)手動(dòng)加鎖
synchronized (list) {
    Iterator<String> iterator = list.iterator();
    while (iterator.hasNext()) {
        String item = iterator.next();
        if (item.equals("A")) {
            iterator.remove(); // 安全地移除元素
        }
    }
}

小結(jié):

使用迭代器提供的remove()方法、避免在for-each中直接修改集合,或選擇適合的集合類型(如CopyOnWriteArrayList),可以有效避免此類異常。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java通俗易懂系列設(shè)計(jì)模式之建造者模式

    Java通俗易懂系列設(shè)計(jì)模式之建造者模式

    這篇文章主要介紹了Java通俗易懂系列設(shè)計(jì)模式之建造者模式,對設(shè)計(jì)模式感興趣的讀者,一定要看一下
    2021-04-04
  • Java內(nèi)存模型及其作用詳解

    Java內(nèi)存模型及其作用詳解

    在現(xiàn)代計(jì)算機(jī)系統(tǒng)中,尤其是多處理器架構(gòu)下,每個(gè)處理器都有自己的高速緩存,而主內(nèi)存(RAM)是所有處理器共享的數(shù)據(jù)存儲(chǔ)區(qū)域,這篇文章主要介紹了深入理解Java內(nèi)存模型及其作用,需要的朋友可以參考下
    2024-04-04
  • Mybatis中TypeHandler使用小結(jié)

    Mybatis中TypeHandler使用小結(jié)

    MyBatis的TypeHandler是一個(gè)強(qiáng)大的機(jī)制,它為我們提供了一種靈活的方式來處理Java類型與數(shù)據(jù)庫類型之間的轉(zhuǎn)換,本文主要介紹了Mybatis中TypeHandler使用小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • java的Console類的使用方法及實(shí)例

    java的Console類的使用方法及實(shí)例

    這篇文章主要介紹了java的Console類的使用方法及實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • JAVA 對象創(chuàng)建與對象克隆

    JAVA 對象創(chuàng)建與對象克隆

    這篇文章主要介紹了JAVA 對象創(chuàng)建與對象克隆,new 創(chuàng)建、反射、克隆、反序列化,克隆它分為深拷貝和淺拷貝,通過調(diào)用對象的 clone方法,進(jìn)行對象的克隆,下面來看看文章的詳細(xì)內(nèi)容吧
    2022-02-02
  • SpringBoot集成MinIO實(shí)現(xiàn)大文件分片上傳的示例代碼

    SpringBoot集成MinIO實(shí)現(xiàn)大文件分片上傳的示例代碼

    本文主要介紹了SpringBoot集成MinIO實(shí)現(xiàn)大文件分片上傳,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-05-05
  • Spring Boot應(yīng)用監(jiān)控的實(shí)戰(zhàn)教程

    Spring Boot應(yīng)用監(jiān)控的實(shí)戰(zhàn)教程

    Spring Boot 提供運(yùn)行時(shí)的應(yīng)用監(jiān)控和管理功能,下面這篇文章主要給大家介紹了關(guān)于Spring Boot應(yīng)用監(jiān)控的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-05-05
  • Java數(shù)據(jù)庫連接_jdbc-odbc橋連接方式(詳解)

    Java數(shù)據(jù)庫連接_jdbc-odbc橋連接方式(詳解)

    下面小編就為大家?guī)硪黄狫ava數(shù)據(jù)庫連接_jdbc-odbc橋連接方式(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • java 同步器SynchronousQueue詳解及實(shí)例

    java 同步器SynchronousQueue詳解及實(shí)例

    這篇文章主要介紹了java 同步器SynchronousQueue詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • java實(shí)現(xiàn)字符串匹配求兩個(gè)字符串的最大公共子串

    java實(shí)現(xiàn)字符串匹配求兩個(gè)字符串的最大公共子串

    這篇文章主要介紹了java實(shí)現(xiàn)求兩個(gè)字符串最大公共子串的方法,詳細(xì)的描述了兩個(gè)字符串的最大公共子串算法的實(shí)現(xiàn),需要的朋友可以參考下
    2016-10-10

最新評論

安徽省| 横峰县| 天水市| 民权县| 靖江市| 商洛市| 麟游县| 天台县| 陈巴尔虎旗| 凤冈县| 宁津县| 渝北区| 剑川县| 星子县| 深水埗区| 越西县| 吕梁市| 宜川县| 永兴县| 万宁市| 逊克县| 巴彦县| 无为县| 乌什县| 日照市| 秦安县| 东丽区| 城步| 张北县| 衡阳县| 中宁县| 木兰县| 万荣县| 繁昌县| 平阳县| 金湖县| 安庆市| 枣强县| 巴青县| 佛山市| 义马市|