Fail Fast and Fail Safe Iterator in Java

2025 年 5 月 2 日 | 阅读 6 分钟

Java 中的迭代器是 Java 集合框架的一部分。它们用于逐个检索元素。Java Collection 支持两种类型的迭代器:快速失败(Fail Fast)和安全失败(Fail Safe)。这些迭代器在异常处理中非常有用。

快速失败迭代器会在暴露失败时立即中止操作,并停止整个操作。相比之下,安全失败迭代器在发生故障时不会中止操作。相反,它会尽可能地避免失败。

在本节中,我们将通过示例讨论快速失败迭代器和安全失败迭代器。此外,我们将了解它们之间的区别。

在理解快速失败和安全失败迭代器之前,让我们先了解并发修改。

并发修改

Java 中的并发修改是指在另一个任务正在处理某个对象时,同时修改该对象。简单来说,并发修改是在另一个线程正在处理对象时修改它们的过程。它会改变数据集合的结构,通过移除、添加或更新集合中元素的值。

并非所有迭代器都支持此行为;某些迭代器的实现可能会抛出 ConcurrentModificationException。

让我们来理解快速失败和安全失败系统。

快速失败和安全失败系统

快速失败系统是一种在报告错误后立即关闭的系统。其中的所有操作都会立即中止。

安全失败系统是一种即使在发生错误或故障后仍能继续运行的系统。这些系统不会立即中止操作;相反,它们会尝试隐藏错误,并尽可能地避免失败。

快速失败迭代器

Java 中的迭代器用于遍历集合的对象。集合返回两种类型的迭代器,要么是快速失败,要么是安全失败。

如果集合的结构被修改,快速失败迭代器会立即抛出 ConcurrentModificationException。结构修改是指在另一个线程遍历集合时,向数据集合中添加、移除或更新元素的值。快速失败迭代器的一些示例包括 ArrayList、HashMap 集合类的迭代器。

工作原理

快速失败迭代器使用一个名为 **modCount** 的内部标志来了解集合的状态,即集合是否已在结构上被修改。每次集合被修改时,modCount 标志都会更新;它会检查下一个值;如果找到,则在创建此迭代器后,modCount 将被修改。它将抛出 ConcurrentModificationException。

请看下面的示例,以理解快速失败迭代器的行为。

输出

LA
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.HashMap$HashIterator.nextNode(HashMap.java:1445)
	at java.util.HashMap$KeyIterator.next(HashMap.java:1469)
	at FailFastDemo.main(FailFastDemo.java:14)

从上面的输出,我们可以注意到以下几点:

  • 如果在迭代集合时对其进行修改,快速失败迭代器会抛出 ConcurrentModificationException。
  • 快速失败迭代器使用原始集合来遍历集合的元素。
  • 它们是内存节约者,不需要额外的内存。
  • ArrayList、HashMap、Vector 类返回快速失败迭代器。

安全失败迭代器

安全失败迭代器与快速失败迭代器正好相反;与它们不同的是,安全失败迭代器不会抛出任何异常,除非它可以在迭代过程中处理集合的修改。这是因为它们操作的是集合对象的副本,而不是原始对象。对原始集合进行的结构性更改会被它们忽略,并影响副本集合,而不是原始集合。因此,原始集合将保持结构不变。

然而,Java SE 文档中并没有“安全失败”这个实际的词语;相反,安全失败被称作非快速失败迭代器。

让我们通过一些示例来理解它的行为。

FailSafeDemo.java

输出

1
7
9
11

从上面的示例中,我们可以看到集合在另一个线程打印结果的同时进行迭代。输出不受其他操作的影响;这意味着创建了集合的独立副本,并在其上执行迭代。

但是,并非所有不使用快速失败迭代器的集合都会创建它的克隆或副本以避免 ConcurrentModificationException。例如,ConcurrentHashMap 不操作对象的独立副本,尽管它不是快速失败的。相反,它使用规范指定为非快速失败迭代器的语义。

考虑下面的示例

FailSafeDemo1.java

输出

EIGHT : 8
FIVE : 5
NINE : 9
ONE : 1
SEVEN : 7

从上面的示例中,我们可以看到我们在另一个线程执行操作的同时正在迭代集合。迭代结果被放置在同一个集合中,这意味着它没有创建对象的独立副本,也没有抛出任何 ConcurrentModificationException。

从上面的示例中,我们可以注意到关于安全失败迭代器的以下几点:

  • 我们可以在迭代集合的同时对其进行修改操作。
  • 它们在迭代过程中不会抛出 ConcurrentModificationException。
  • 安全失败迭代器使用集合的副本遍历元素。
  • 与快速失败不同,它们需要更多的内存,因为它们克隆了集合。
  • 安全失败迭代器的示例包括 ConcurrentHashMap、CopyOnWriteArrayList 等。

快速失败和安全失败迭代器之间的区别

快速失败和安全失败迭代器之间的主要区别在于,安全失败在迭代过程中修改对象时不会抛出任何 ConcurrentModificationException,而快速失败则在这种情况下会抛出异常。这是因为安全失败迭代器操作的是克隆的集合,而不是原始集合。

在不同参数的基础上,它们之间还有一些其他比较。让我们来讨论一下。

比较依据快速失败迭代器安全失败迭代器
Exception在迭代过程中修改对象时,它会抛出 ConcurrentModificationException。它不会抛出异常。
克隆对象在迭代过程中不创建克隆对象。在迭代过程中创建副本或克隆对象。
内存利用在处理过程中需要较少的内存。在处理过程中需要更多的内存。
修改迭代过程中不允许修改。允许在迭代过程中进行修改。
性能它速度很快。它比快速失败稍微慢一些。
示例HashMap、ArrayList、Vector、HashSet 等CopyOnWriteArrayList、ConcurrentHashMap 等。