在下面的代码片段中,为 ArrayList 创建了一个迭代器,之后 ArrayList 的结构发生了变化。然后使用迭代器。ArrayList<Integer> list = new ArrayList<>(2);list.add(1);Iterator<Integer> itr = list.iterator();list.clear();list.add(2);list.add(3);while (itr.hasNext()) { System.out.println(itr.next());}正如预期的那样, aConcurrentModificationException被抛出itr.next()。现在,看看这个:ArrayList<Integer> list = new ArrayList<>(2);list.add(1);int modCount = 1;Iterator<Integer> itr = list.iterator();do { list.clear(); list.add(2); list.add(3); modCount += 3;} while (modCount != 1);while (itr.hasNext()) { System.out.println(itr.next());}几秒后,执行结束,没有任何异常,结果为:23ArrayList 在结构上发生了变化,它的最终状态与第一个代码片段相同,但仍然没有抛出异常,即使在第一个代码片段中它抛出了异常。查看 ArrayList 源代码后,这是意料之中的,因为底层 modCount 是 int 类型。对 ArrayList 进行足够的修改会导致 modCount 溢出,在某个时候返回 1。Iterator 认为 ArrayList 没有改变,因此不会抛出异常。在 Java SE 12 文档中,对于 ArrayList 类,声明如下:请注意,无法保证迭代器的快速失败行为,因为一般来说,在存在非同步并发修改的情况下不可能做出任何硬性保证。很明显,迭代器可能会犯“错误”,但这又是针对非同步并发修改的,对吧?但是,上面的第二个片段是同步的。这里发生了什么?第二个片段中的 Iterator 应该是这样的吗?如果是这样,那么 long 类型的 modCount 会不会好一点(问题仍然不会消失,但是 2^64 + 1 修改在合理的时间内执行是不可行的)。这也可以在其他Collections 中观察到,它们使用相同的 int modCount 机制。
3 回答
ITMISS
TA贡献1871条经验 获得超8个赞
您所做的一切都是创建一个场景,在该场景中,modCount 最终会环绕,直到它处于某种状态以反映没有发生修改,因此没有 CME。也许您希望 modCount 是 long 而不是 int。
潇湘沐
TA贡献1816条经验 获得超6个赞
稍加思索后,我得出结论:
如果 Iterator 抛出 a
ConcurrentModificationException
,则源在正在进行的迭代中进行了结构修改。如果源在其迭代器处于正在进行的迭代中时在结构上被修改,则该迭代器可能会或可能不会抛出
ConcurrentModificationException
.
考虑到这一点,迭代器尝试抛出 a 的唯一原因ConcurrentModificationException
是帮助调试。总比没有好,尤其是在一般情况下。
在 modCount 中使用 long 而不是 int 将增加 may 的机会并减少may not在 2 中的机会。但只有使用可以容纳无限位数的类型才能使may有机会 1.0 和may没有0.0 的机会(当然至少在那种架构下)。因此,无论 modCount 使用多少个有限位,它都不会渐近地产生影响。
添加回答
举报
0/150
提交
取消