该说明的作者是来自克拉科夫(波兰)的软件开发人员 Grzegorz Mirek。大约 6 年前,他还在读大学时就开始使用 Java 进行开发,从那时起,他就一直在不知疲倦地磨练自己在该领域的技能。他对 JVM 性能和优化特别感兴趣,这是他在博客上主要写的内容。
一些最流行的 Java 面试问题包括: 快速失败迭代器和故障安全迭代器之间有什么区别? 对此最简单的答案是: 如果集合在迭代期间发生更改,快速失败迭代器将引发 ConcurrentModificationException,但故障安全迭代器不会。 虽然这听起来很有意义,但仍不清楚面试官所说的“故障安全”是什么意思?Java 语言规范没有定义与迭代器相关的术语。然而,有四种竞争性修改策略。
竞争性改装
首先,让我们定义什么是竞争(或并行)修改。假设我们有一个集合,当迭代器处于活动状态时,会发生一些并非来自该迭代器的更改。在这种情况下,我们得到了竞争性的修改。让我给你一个简单的例子:假设我们有几个线程。第一个线程进行迭代,第二个线程从同一集合中插入或删除元素。然而,在单线程环境中运行时, 我们可能会得到ConcurrentModificationException :List<String> cities = new ArrayList<>();
cities.add(“Warsaw”);
cities.add(“Prague”);
cities.add(“Budapest”);
Iterator<String> cityIterator = cities.iterator();
cityIterator.next();
cities.remove(1);
cityIterator.next(); // генерирует ConcurrentModificationException
快速失败
上面的代码片段是快速失败迭代器的示例。正如您所看到的,当尝试从迭代器检索第二个元素时,抛出了ConcurrentModificationException。迭代器如何知道集合自创建以来已被修改?例如,集合可能有一个日期/时间戳,例如lastModified。创建迭代器时,应该复制该字段并将其存储在迭代器对象中。然后,每次调用next()方法时,您只需将集合中的LastModified值与迭代器中的副本进行比较即可。例如,在ArrayList类的实现中使用了非常相似的方法。它有一个实例变量modCount存储列表被修改的次数:final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
需要注意的是,快速失败迭代器在最佳组合的基础上运行,这意味着不能保证在并发修改时会抛出ConcurrentModificationException 。因此您不应该依赖它们 - 相反,它们应该用于检测错误。大多数非并发集合提供快速失败迭代器。
弱一致性
java.util.concurrent包中的大多数并发集合(例如ConcurrentHashMap和大多数Queue)都提供弱一致迭代器。该术语的含义在文档中有很好的解释:- 它们可以与其他操作同时处理
- 他们从不抛出ConcurrentModificationException
- 它们保证在迭代器创建时遍历现有元素一次,并且可以(但不要求)反映后续修改。
GO TO FULL VERSION