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

Java導(dǎo)致ConcurrentModificationException所有原因

 更新時(shí)間:2026年01月20日 10:49:16   作者:猩火燎猿  
ConcurrentModificationException是Java集合框架拋出的運(yùn)行時(shí)異常,表示集合在遍歷過程中被結(jié)構(gòu)性修改了,導(dǎo)致迭代器無法保證一致性,下面就來介紹一下幾種原因,感興趣的可以了解一下

1. 什么是 ConcurrentModificationException?

它是 Java 集合框架拋出的運(yùn)行時(shí)異常,表示集合在遍歷過程中被結(jié)構(gòu)性修改了,導(dǎo)致迭代器無法保證一致性。

2. 異常觸發(fā)的底層機(jī)制

Java 集合(如 ArrayList、HashSet 等)在創(chuàng)建迭代器時(shí),會(huì)記錄集合的結(jié)構(gòu)性修改次數(shù)(modCount)。每次集合結(jié)構(gòu)發(fā)生變化(如 add、remove),modCount 增加。迭代器內(nèi)部有一個(gè) expectedModCount,每次調(diào)用 next()/hasNext() 時(shí),會(huì)檢查 modCount 是否和 expectedModCount 一致。如果不一致,就拋出 ConcurrentModificationException。

3. 導(dǎo)致 ConcurrentModificationException 的所有常見原因

3.1 遍歷過程中直接修改集合

錯(cuò)誤代碼示例:

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
for (String s : list) {
    if (s.equals("b")) {
        list.remove(s); // 錯(cuò)誤!遍歷時(shí)直接修改集合
    }
}

原因: for-each 底層用的是 iterator,直接用 list.remove() 修改集合,modCount 改變,expectedModCount 沒變,拋異常。

3.2 用 Iterator 遍歷時(shí),直接用集合的 add/remove 方法修改集合

Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if (s.equals("b")) {
        list.remove(s); // 錯(cuò)誤!應(yīng)使用 it.remove()
    }
}

3.3 多線程并發(fā)修改集合

一個(gè)線程遍歷集合,另一個(gè)線程同時(shí)修改集合(結(jié)構(gòu)性操作),也會(huì)拋異常。

示例:

List<Integer> list = new ArrayList<>(Arrays.asList(1,2,3));
new Thread(() -> {
    for (Integer i : list) {
        System.out.println(i);
    }
}).start();
 
new Thread(() -> {
    list.add(4); // 并發(fā)修改
}).start();

3.4 在 for-each 循環(huán)中調(diào)用集合的 remove/add

for (String s : list) {
    list.remove(s); // 錯(cuò)誤
}

正確做法: 用 Iterator 的 remove 方法。

3.5 對 Map 的 keySet/values/entrySet 進(jìn)行遍歷時(shí)修改 Map

Map<String, Integer> map = new HashMap<>();
map.put("a", 1); map.put("b", 2);
for (String key : map.keySet()) {
    map.remove(key); // 錯(cuò)誤
}

3.6 迭代器遍歷時(shí),集合結(jié)構(gòu)被外部方法修改

如果在遍歷過程中調(diào)用了會(huì)修改集合結(jié)構(gòu)的方法(即使不是直接在循環(huán)體里),也會(huì)導(dǎo)致異常。

3.7 使用 fail-fast 集合(如 ArrayList、HashSet、HashMap)遍歷時(shí)結(jié)構(gòu)性修改

Java 的大多數(shù)集合都是 fail-fast 的(快速失?。?,即檢測到并發(fā)修改就立即拋異常。

4. 什么是結(jié)構(gòu)性修改?

結(jié)構(gòu)性修改指的是影響集合元素?cái)?shù)量或排列的操作,比如 add、remove、clear、put(Map),而僅僅修改元素內(nèi)容(如 set(index, value))不算結(jié)構(gòu)性修改。

5. 如何避免 ConcurrentModificationException?

用 Iterator 的 remove 方法刪除元素

Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if (s.equals("b")) {
        it.remove();
    }
}

用并發(fā)安全集合
如 CopyOnWriteArrayList、ConcurrentHashMap,這些集合不會(huì)拋 ConcurrentModificationException。

遍歷前收集要?jiǎng)h除的元素,遍歷后統(tǒng)一刪除

List<String> toRemove = new ArrayList<>();
for (String s : list) {
    if (條件) toRemove.add(s);
}
list.removeAll(toRemove);

使用 ListIterator 的 add/remove/set 方法

6. 其他補(bǔ)充

  • ConcurrentModificationException 只是 fail-fast 機(jī)制的一部分,不能保證百分百檢測到所有并發(fā)修改。
  • 在多線程環(huán)境下,優(yōu)先使用并發(fā)集合或加鎖處理。

7. 總結(jié)

所有原因本質(zhì):
遍歷過程中集合結(jié)構(gòu)被直接或間接修改,導(dǎo)致迭代器檢測到不一致。

常見場景:

  • for-each 循環(huán)中直接 add/remove
  • Iterator 遍歷時(shí)集合 add/remove
  • 多線程并發(fā)修改
  • Map 的 keySet/values/entrySet 迭代時(shí)修改 Map

避免方式:

  • 用 iterator.remove()
  • 用并發(fā)集合
  • 遍歷前收集、后批量修改

8. 底層原理再深入

8.1 modCount 和 expectedModCount

  • 每個(gè) fail-fast 集合(如 ArrayList、HashMap)內(nèi)部有一個(gè) modCount 字段,代表結(jié)構(gòu)性修改次數(shù)。
  • 創(chuàng)建迭代器時(shí),迭代器保存一份 expectedModCount。
  • 每次迭代器操作(如 next()、remove())時(shí),都會(huì)檢查 modCount 是否和 expectedModCount 一致。
  • 如果不一致,說明集合被外部修改,拋出 ConcurrentModificationException

源碼片段(以 ArrayList 為例):

public E next() {
    checkForComodification(); // 檢查
    ...
}
final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

9. 特殊集合的處理

9.1 并發(fā)集合不會(huì)拋出該異常

  • CopyOnWriteArrayList、ConcurrentHashMap 等并發(fā)集合,內(nèi)部機(jī)制不同,不會(huì) fail-fast。
  • 例如,CopyOnWriteArrayList 每次修改都會(huì)復(fù)制一份新數(shù)據(jù),迭代器遍歷的是舊快照,不關(guān)心后續(xù)修改。

9.2 老的 Vector、Hashtable

  • 這些集合的方法都加了同步鎖(synchronized),不會(huì)拋出 ConcurrentModificationException,但性能較低。

10. 實(shí)際項(xiàng)目中的規(guī)避策略

10.1 單線程環(huán)境

  • 刪除元素用 iterator.remove()。
  • 遍歷前收集需要?jiǎng)h除的元素,遍歷后統(tǒng)一刪除。
  • 不要在 for-each 或普通 for 循環(huán)中直接 remove/add。

10.2 多線程環(huán)境

  • 使用并發(fā)集合(如 CopyOnWriteArrayList、ConcurrentHashMap)。
  • 使用同步塊(synchronized)保護(hù)遍歷和修改操作。
  • 分批處理:先收集需要修改的數(shù)據(jù),后統(tǒng)一操作。

10.3 遍歷 Map 的安全刪除

  • 用 Iterator<Map.Entry<K,V>> 遍歷,然后用 iterator.remove() 刪除當(dāng)前 entry。
Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry<String, Integer> entry = it.next();
    if (entry.getValue() < 10) {
        it.remove();
    }
}

11. 排查和調(diào)試方法

11.1 查看異常堆棧

  • 異常堆棧會(huì)指向集合的迭代器方法(如 next()),分析調(diào)用鏈,定位哪里修改了集合。

11.2 檢查所有集合修改點(diǎn)

  • 搜索代碼中所有對集合的結(jié)構(gòu)性操作(add、remove、clear等),看是否在遍歷期間被調(diào)用。

11.3 多線程場景

  • 檢查是否有線程并發(fā)修改集合,必要時(shí)加鎖或用并發(fā)集合。

12. 面試延伸問題

  1. 什么是 fail-fast?什么是 fail-safe?舉例說明。
    • fail-fast:檢測到并發(fā)修改立即拋異常(如 ArrayList)。
    • fail-safe:迭代器遍歷的是快照,不拋異常(如 CopyOnWriteArrayList)。
  2. 如何安全地在遍歷過程中刪除集合元素?
    • 用 iterator.remove()。
  3. ConcurrentModificationException 一定能檢測到所有并發(fā)修改嗎?
    • 不能,只能檢測到部分典型場景。
  4. 為什么并發(fā)集合不會(huì)拋 ConcurrentModificationException?
    • 并發(fā)集合設(shè)計(jì)了特殊機(jī)制,如快照、分段鎖等,保證遍歷安全。

13. 真實(shí)案例分析

案例:批量刪除數(shù)據(jù)庫記錄時(shí)同步維護(hù)緩存集合

假設(shè)你有一個(gè)緩存 List,批量刪除數(shù)據(jù)庫記錄后也要同步刪除 List 中的元素:

// 錯(cuò)誤做法,可能拋異常
for (User user : cacheList) {
    if (shouldDelete(user)) {
        cacheList.remove(user);
    }
}
 
// 正確做法
Iterator<User> it = cacheList.iterator();
while (it.hasNext()) {
    User user = it.next();
    if (shouldDelete(user)) {
        it.remove();
    }
}

14. 代碼示例:多線程并發(fā)修改

List<Integer> list = new ArrayList<>();
for (int i = 0; i < 100; i++) list.add(i);
 
Thread t1 = new Thread(() -> {
    for (int i : list) {
        // 遍歷
    }
});
Thread t2 = new Thread(() -> {
    list.remove(50); // 并發(fā)修改
});
t1.start(); t2.start();
// 可能拋 ConcurrentModificationException

15. 總結(jié)

  • ConcurrentModificationException 是集合 fail-fast 機(jī)制的體現(xiàn)。
  • 本質(zhì)原因是遍歷期間結(jié)構(gòu)性修改集合。
  • 規(guī)避方法:用 iterator.remove(),用并發(fā)集合,多線程加鎖,遍歷后統(tǒng)一修改。
  • 多線程場景優(yōu)先用并發(fā)集合,普通集合加鎖也可。
  • 面試常問底層原理、fail-fast 與 fail-safe、實(shí)際場景規(guī)避。

到此這篇關(guān)于Java導(dǎo)致ConcurrentModificationException所有原因的文章就介紹到這了,更多相關(guān)Java導(dǎo)致ConcurrentModificationException原因內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解SpringMVC在IDEA中的第一個(gè)程序

    詳解SpringMVC在IDEA中的第一個(gè)程序

    Spring MVC 屬于Spring Framework的一部分,是一種Spring框架內(nèi)置的MVC的實(shí)現(xiàn)。這篇文章主要介紹了SpringMVC在IDEA中的第一個(gè)程序,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • OutOfMemoryError內(nèi)存不足和StackOverflowError堆棧溢出示例詳解

    OutOfMemoryError內(nèi)存不足和StackOverflowError堆棧溢出示例詳解

    這篇文章主要為大家介紹了OutOfMemoryError內(nèi)存不足和StackOverflowError堆棧溢出示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • SpringBoot?實(shí)現(xiàn)流控的操作方法

    SpringBoot?實(shí)現(xiàn)流控的操作方法

    本文介紹了限流算法的基本概念和常見的限流算法,包括計(jì)數(shù)器算法、漏桶算法和令牌桶算法,還介紹了如何在Spring?Boot中使用Guava庫和自定義注解以及AOP實(shí)現(xiàn)接口限流功能,感興趣的朋友一起看看吧
    2024-12-12
  • 解決Java原生壓縮組件不支持中文文件名亂碼的問題

    解決Java原生壓縮組件不支持中文文件名亂碼的問題

    本篇文章主要介紹了解決Java原生壓縮組件不支持中文文件名亂碼的問題,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-03-03
  • Java中的CopyOnWriteArrayList你了解嗎

    Java中的CopyOnWriteArrayList你了解嗎

    CopyOnWriteArrayList是Java集合框架中的一種線程安全的List實(shí)現(xiàn),這篇文章主要來和大家聊聊CopyOnWriteArrayList的簡單使用,需要的可以參考一下
    2023-06-06
  • JAVA“無法驗(yàn)證證書。將不執(zhí)行該應(yīng)用程序?!碧崾窘鉀Q辦法

    JAVA“無法驗(yàn)證證書。將不執(zhí)行該應(yīng)用程序?!碧崾窘鉀Q辦法

    這篇文章主要給大家介紹了關(guān)于JAVA“無法驗(yàn)證證書,將不執(zhí)行該應(yīng)用程序”提示的解決辦法,要解決Java無法驗(yàn)證證書的問題,可以嘗試下本文的方法,需要的朋友可以參考下
    2024-03-03
  • Java遞歸以及根據(jù)節(jié)點(diǎn)取子集合方式

    Java遞歸以及根據(jù)節(jié)點(diǎn)取子集合方式

    文章介紹了Java中遞歸的使用方法,包括如何根據(jù)節(jié)點(diǎn)構(gòu)建樹形結(jié)構(gòu)以及如何反向遞歸獲取所有子節(jié)點(diǎn),提供了遞歸方法的參數(shù)解釋和示例代碼,希望對大家有所幫助
    2024-12-12
  • SpringBoot使用Graylog日志收集的實(shí)現(xiàn)示例

    SpringBoot使用Graylog日志收集的實(shí)現(xiàn)示例

    Graylog是一個(gè)生產(chǎn)級別的日志收集系統(tǒng),集成Mongo和Elasticsearch進(jìn)行日志收集,這篇文章主要介紹了SpringBoot使用Graylog日志收集的實(shí)現(xiàn)示例,感興趣的小伙伴們可以參考一下
    2019-04-04
  • idea修改只讀/可寫狀態(tài)全過程

    idea修改只讀/可寫狀態(tài)全過程

    本文記錄了解決IntelliJ IDEA(打開文件只讀問題的過程,在設(shè)置中找到Editor下的Reader Mode,取消勾選選第一個(gè)可選框即可解決問題,同時(shí)提到該選項(xiàng)看起來很好看,本文僅供參考,希望能對讀者有所幫助
    2026-05-05
  • Spring超詳細(xì)講解創(chuàng)建BeanDefinition流程

    Spring超詳細(xì)講解創(chuàng)建BeanDefinition流程

    Spring在初始化過程中,將xml中定義的對象解析到了BeanDefinition對象中,我們有必要了解一下BeanDefinition的內(nèi)部結(jié)構(gòu),有助于我們理解Spring的初始化流程
    2022-06-06

最新評論

固原市| 定远县| 宿州市| 五大连池市| 达拉特旗| 临猗县| 板桥市| 金湖县| 北流市| 巫山县| 平陆县| 邢台县| 佛山市| 邢台县| 东山县| 安阳县| 阿荣旗| 崇仁县| 娄底市| 台山市| 台湾省| 澄江县| 万山特区| 突泉县| 固镇县| 巴塘县| 临武县| 繁昌县| 迁西县| 永城市| 洪泽县| 边坝县| 双牌县| 普定县| 建湖县| 西畴县| 扎赉特旗| 巨野县| 锡林浩特市| 霍邱县| 大余县|