在下面的代码片段中,为 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());
}
几秒后,执行结束,没有任何异常,结果为:
2
3
ArrayList 在结构上发生了变化,它的最终状态与第一个代码片段相同,但仍然没有抛出异常,即使在第一个代码片段中它抛出了异常。
查看 ArrayList 源代码后,这是意料之中的,因为底层 modCount 是 int 类型。对 ArrayList 进行足够的修改会导致 modCount 溢出,在某个时候返回 1。Iterator 认为 ArrayList 没有改变,因此不会抛出异常。
在 Java SE 12 文档中,对于 ArrayList 类,声明如下:
请注意,无法保证迭代器的快速失败行为,因为一般来说,在存在非同步并发修改的情况下不可能做出任何硬性保证。
很明显,迭代器可能会犯“错误”,但这又是针对非同步并发修改的,对吧?但是,上面的第二个片段是同步的。
这里发生了什么?第二个片段中的 Iterator 应该是这样的吗?如果是这样,那么 long 类型的 modCount 会不会好一点(问题仍然不会消失,但是 2^64 + 1 修改在合理的时间内执行是不可行的)。
这也可以在其他Collections 中观察到,它们使用相同的 int modCount 机制。
缥缈止盈
ITMISS
潇湘沐
相关分类