Sunday面试指南

Java 的 fail-fast 和 fail-safe 是什么?为什么遍历 ArrayList 时删除元素会报错?

下面是一段教学用的模拟面试。

🧑‍💻 面试官: 遍历 ArrayList 时,为什么删除元素会报错?

🙋‍♂️ 我: 因为 fail-fast 检测到了多线程修改。

🧑‍💻 面试官: 只有一个线程,for-each 里直接调用 list.remove,也会有问题吗?

🙋‍♂️ 我: 会,迭代器外的结构修改也会被发现。

🧑‍💻 面试官: 那没抛异常,是不是就能证明这段遍历安全?

fail-fast 是尽早暴露错误的检查,不是保证并发安全的护栏。它甚至不一定每次都报错。

面试速答(60 秒版)

ArrayList 的迭代器是 fail-fast 的。创建迭代器时,它会记录预期的结构修改状态;后续迭代发现不符合预期的结构变化,通常会抛 ConcurrentModificationException。

这不只发生在多线程。单线程遍历时,从集合本身删除元素,也可能破坏迭代器的预期。

如果迭代契约支持,可以通过迭代器自己的 remove 删除当前元素,或者使用集合提供的合适批量操作。但这些方式不自动使集合变成线程安全的。

fail-fast 只是尽力检查,不能依靠“有没有抛异常”判断并发正确性。常说的 fail-safe 也不是 Java 集合统一的规范术语,还要区分快照迭代和弱一致迭代。

fail-fast 是检查,不是锁

图:fail-fast 是检查,不是锁。

知识点详解:迭代器为什么要知道列表有没有被改过?

删除元素,影响的不只是那个值

假设列表里依次是 A、B、C。迭代器准备从当前位置继续往后走,此时外部删除了 A,后面的元素位置就变了。

如果仍按原来的位置继续,可能漏看元素,也可能产生其他不符合预期的行为。因此,迭代器需要知道列表结构是否在它不知情的情况下发生变化。

ArrayList 使用修改计数一类的机制帮助检测。迭代器记录预期值,集合结构变化后,两者可能不一致。这个计数是检测手段,不是锁,也不是并发事务。

为什么单线程也会报 ConcurrentModificationException?

异常名字里的 Concurrent 容易让人以为“必须两个线程同时操作”。实际上,单线程代码也可能一边使用迭代器,一边通过另一个入口修改集合。

Java 的 for-each 遍历 ArrayList 时,背后通常用迭代器。此时直接调用 list.remove,并没有走这个迭代器认可的删除路径。

而迭代器自己的 remove 知道它刚刚返回了哪个元素,可以同时调整自身状态。使用前还要遵守调用顺序,例如不能还没 next 就 remove,也不能对同一次 next 连续 remove。

通过谁删除,契约不同

图:通过谁删除,契约不同。

不是每次内容变化都叫结构修改

通常,增加、删除元素会改变集合结构;用 set 替换某个已有位置的值,并不等同于增加或删除结构。

但这不能被解释成“set 在并发下就安全”。迭代器的结构检查与多个线程如何安全访问内容,是两层问题。具体契约见 ArrayList 官方文档。

文档也明确提醒 fail-fast 是 best-effort。不能为了等它抛异常,放任错误修改继续发生。

fail-safe 这句话还需要再拆开

一些面试资料把“不抛这个异常”都叫 fail-safe,容易把不同机制混在一起。

CopyOnWriteArrayList 的迭代器通常观察创建时的快照,后续写入不会反映进这份快照。ConcurrentHashMap 则提供弱一致的遍历契约,并不等于每次都复制完整数据。

所以,先说清楚具体集合,再解释它看到的是哪一份数据。不能只背“一个快,一个安全”。

本题描述 Java 集合契约,不提供名称相似却语义不同的 TS、Python 迭代器替代代码。

面试官继续追问

捕获异常后重新遍历,能当作并发方案吗?

不能。报错不是一个可靠的冲突协议;还有可能根本没报。应该按需求选择同步、快照或具有明确并发契约的集合。

怎么测试删除逻辑?

至少检查连续需要删除的元素、首尾元素、空列表,以及是否漏掉相邻项。有并发访问时,还要单独验证同步设计。

面试速记卡

  • fail-fast:尽力检测非预期结构变化。
  • 单线程:也可能在迭代器外修改集合。
  • 删除:使用受支持的迭代器删除或合适批量操作。
  • 无异常:不代表线程安全或遍历正确。
  • 并发集合:快照与弱一致是不同契约。

公司面试真题

真题根据求职者公开面经整理,题意经过概括,非逐字原话或公司官方题库;本文为 Sunday 的独立解析。

  • 京东 · Java后台 · 校招

    Java 集合的快速失败与安全失败有什么差异?(题意整理)

    京东 Java 后台三面凉经 ↗
    原帖编辑于 2019-08-23(历史校招面经)

浏览公司面试真题 →
简历汪永久免费在线制作简历,模板直接套用、导出无水印,永久免费、下载免费,不需要付费解锁任何功能。去写简历