Java 中的迭代器iterators是一种帮助开发人员避免运行时异常的简单机制。让我们探索快速失败和安全失败迭代器之间的区别。
Java 中的迭代器通常期望对它们循环遍历的数据结构进行独占访问。如果另一个线程在迭代器处理对象时尝试修改对象集合,则会抛出 ConcurrentModificationException。由于与管理任意调度的线程相关的复杂性,对 ConcurrentModificationException 进行故障排除可能很困难。幸运的是,Java 提供了一种简单的机制来帮助开发人员避免这种异常。
两种失败模式
Java 迭代器分为两个独立的阵营失败模式:fail-fast 和fail-safe:
fail-fast快速失败是引发 ConcurrentModificationException 的原因;而fail-safe故障安全的迭代器不会抛出这个异常。当然,决定使用fail-safe故障安全迭代器需要更多的考虑,而不仅仅是避免运行时异常的愿望。
ArrayList 和 HashMap 类具有fail-fast快速失败的迭代器;CopyOnWriteArrayList 和 ConcurrentHashMap 类使用fail-safe故障安全迭代器。
故障安全迭代器的好处
当感兴趣的集合发生结构变化时,故障安全迭代器不会抛出异常,因为它们与克隆一起工作,而不是集合本身。因此,故障安全迭代器会忽略底层集合发生的更改。
故障安全迭代器提供的主要好处是它们最大限度地减少了抛出异常的可能性。但这种好处是有代价的,其中之一是增加了内存需求。任何时候JVM克隆对象时,都需要额外的内存空间。对于大型集合,内存占用量可能很大。
故障安全迭代器的缺点
如果 JVM 分配和释放越来越多的内存,垃圾收集例程将不得不更频繁地运行。这将增加 CPU 负载,触发 stop-the-world 事件并降低整体应用程序性能。
另一个缺点是它提供的结果可能与底层集合不同步。毕竟,克隆不会反映在进行克隆后底层集合发生的任何更改。如果您的应用程序不能接受这种不一致,则不应使用故障安全迭代器。
两者比较
如果目标是避免异常,系统资源充足,并且在迭代器运行时对底层集合类的更新不会对应用程序的业务需求产生负面影响,那么故障安全迭代器是一个明智的选择。否则,坚持使用快速失败的迭代器。