Iterator迭代中的快速失败

在运用迭代器遍历集合时,会出现以下异常情况:
     java.util.ConcurrentModificationException
我们可以在帮助文档看java.util包下查找此类异常:

在这里插入图片描述
(1)我们可以先看下并发修改 AND 快速失败
某个线程在 Collection 上进行迭代时,通常不允许另一个线性修改该 Collection。(我们在用iterator遍历时,使用集合的方法操作集合)
通常在这些情况下,迭代的结果是不确定的。如果检测到这种行为,一些迭代器实现(包括 JRE 提供的所有通用 collection 实现)可能选择抛出此异常。执行该操作的迭代器称为快速失败 迭代器,因为迭代器很快就完全失败,而不会冒着在将来某个时间任意发生不确定行为的风险。
(2)在源码中看抛出此异常的情况:
在这里插入图片描述
我们可以看出在next()方法中先进行在这里插入图片描述
当两者不相等时抛出此类异常:
迭代器在遍历集合的时候会使用一个 modCount 变量:当对集合进行操作时会修改 modCount 的值,当每次调用 next()方法时,首先判断是否==expctedModCount,而当调用add()或remove()方法时会修改 modCount的值,若不相等则抛出此异常。
在这里插入图片描述
(3)解决方法:
迭代器中 remove()方法,当在迭代过程中调用iterator中的方法对集合进行修改操作,则不会抛出异常。

### 关于 Java 迭代快速失败机制及其与同步方法的关系 Java 中的迭代器通常被描述为具有“快速失败(fail-fast) 的特性。这意味着当某个线程尝试通过迭代器访问集合时,如果发现该集合已被其他线程修改,则会立即抛出 `ConcurrentModificationException` 异常[^1]。 然而,在某些情况下,迭代器可能表现得并非完全遵循这一原则。以下是原因分析: #### 1. **快速失败机制的工作原理** 快速失败的核心在于维护一个名为 `modCount` 的计数器变量。每次对集合执行结构性修改(如添加或移除元素),都会使 `modCount` 自增。而迭代器内部则有一个对应的副本变量 `expectedModCount`。在每次调用迭代器的方法之前,都会检查这两个值是否相等。如果不相等,则认为发生了并发修改并抛出异常。 但是需要注意的是,这种检测仅限于结构上的变化,并无法感知到由外部同步控制所引发的行为差异。 #### 2. **同步方法的影响** 尽管快速失败机制能够有效捕获未经协调的多线程操作带来的问题,但它本身并不提供任何实际意义上的线程安全性保障。换句话说,即使启用了同步机制来保护共享资源免受竞争条件影响,也无法改变迭代器本身的 fail-fast 行为逻辑。 具体来说: - 如果多个线程试图同时读写同一个集合实例,那么即便这些操作都被包裹进了显式的 synchronized 块里头,只要其中一个线程进行了可能导致 modCount 变化的动作,另一个正在运行中的迭代过程依旧会被中断并触发 ConcurrentModificationException。 这是因为同步仅仅是为了防止数据竞争现象的发生;它并不能阻止已经启动但尚未完成其生命周期的一个特定迭代序列察觉到来自其它地方发生的变更事件[^3]。 #### 3. **为何有时看起来不像快速失败?** 有时候开发者可能会观察到这样的情况——即明明存在违反预期的操作却并未见到相应的异常提示出来。这主要是因为以下几个因素造成的误解或者特殊情况所致: - 并非所有的更改都会引起 modCount 的增加。例如,替换现有条目的值并不会算作一次新的结构调整活动,因而也不会激活快速失败机制。 - 当前实现版本可能存在一定的延迟效应。也就是说,虽然理论上应该马上报错,但实际上由于种种缘故使得错误报告有所滞后甚至完全没有发生。 - 使用 CopyOnWriteArrayList 或者 ConcurrentHashMap 等特殊类型的容器代替普通的 List/Set/Map 结构体时,它们各自具备不同的处理策略以应对高并发场景下的需求,从而规避掉了传统意义上所谓的 “快速失败”。 综上所述,尽管大多数时候我们习惯性地把 Iterator 称之为 fast-fail 类型组件之一,但在涉及复杂环境尤其是高度动态化且频繁交互的数据集管理场合下,仍需谨慎对待可能出现的各种例外情形。 ```java // 示例代码展示如何手动同步但仍遭遇 CME 错误 public class SyncExample { public static void main(String[] args) throws InterruptedException { List<Integer> list = new ArrayList<>(); // 添加初始元素 for (int i=0;i<10;i++) {list.add(i);} Thread t1 = new Thread(() -> { synchronized(list){ try{ System.out.println("Thread T1 started"); Iterator<Integer> it = list.iterator(); while(it.hasNext()){ Integer num = it.next(); if(num.equals(5)){ list.remove(num); } } }catch(Exception e){ e.printStackTrace(); } System.out.println("Thread T1 finished"); } }); Thread t2 = new Thread(() -> { synchronized(list){ try{ System.out.println("Thread T2 started"); Random rand = new Random(); int sleepTime = rand.nextInt(500)+50; Thread.sleep(sleepTime); list.add(-999); // Structural modification System.out.println("Thread T2 added element and sleeping..."); Thread.sleep(800); System.out.println("Thread T2 finished"); } catch (InterruptedException ie){ ie.printStackTrace(); } } }); t1.start();t2.start(); t1.join();t2.join(); } } ``` 上述例子展示了即使有同步措施也不能避免潜在的 CME 风险。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值