fail-fast机制与fail-safe机制

你好,我是吴计可师,一个工作十多年的后端开发,曾就职京东、阿里等多家互联网头部企业。

点击下方👇关注公众号,带你一起复习后端技术,看看面试考点,补充积累技术知识,每天都为面试准备积累

文章可能会比较长,主要解析的非常详解,或涉及一些底层知识,供面试高阶难度用。可以根据自己实际理解情况合理取舍阅读


fail-fast 和 fail-safe 是两种常见的错误处理机制,它们在并发编程和集合框架中的应用有着不同的行为方式。主要的区别在于它们在面对并发修改时的反应:fail-fast 会快速报告错误,而 fail-safe 会尽力避免抛出异常,允许继续操作。


01
fail-fast(快速失败)


定义:fail-fast 是指在检测到对集合的结构性修改时,快速抛出异常并终止操作。集合或容器会在发生并发修改(例如,结构性修改)时立即抛出 ConcurrentModificationException 异常。

工作原理:当你在遍历 fail-fast 集合时,如果集合的结构发生了变化(例如,元素被删除或添加),那么会立即抛出异常。这样可以尽早发现错误,避免错误在程序中传播。

常见应用:ArrayList、HashMap、LinkedList、HashSet 等集合类在进行迭代时,通常是 fail-fast 的。这些集合会在结构修改时触发异常。

优缺点:

  • 优点:当出现并发修改时,它能快速报告错误,防止错误被隐性地传播。

  • 缺点:并发修改会导致程序抛出异常,不能继续执行,需要进行额外的异常处理。


List<String> list = new ArrayList<>();list.add("A");list.add("B");Iterator<String> iterator = list.iterator();list.remove(0);  // 修改集合结构while (iterator.hasNext()) {    System.out.println(iterator.next());  // 会抛出 ConcurrentModificationException}


02
fail-safe(安全失败)


定义:fail-safe 是指在面对并发修改时,集合不会立即抛出异常,而是以某种方式“容忍”并发修改。它通常通过在每次遍历时使用副本或其他机制来确保迭代过程不受并发修改的影响。

工作原理:fail-safe 集合通常在遍历时使用的是集合的副本,或者它使用某种机制使得遍历操作与修改操作分离。即使在遍历过程中发生了结构性修改,迭代操作也不会受影响。

常见应用:ConcurrentHashMap、CopyOnWriteArrayList、CopyOnWriteArraySet 等类是 fail-safe 的。它们通过使用复制机制、写时复制等策略,允许在并发修改的情况下进行安全的迭代。

优缺点:

  • 优点:可以在并发环境中安全地遍历集合,修改集合时不会抛出异常。

  • 缺点:可能会导致额外的内存开销和性能损失,因为它们需要保持一个副本或者通过其他机制来避免并发修改的影响。

List<String> list = new CopyOnWriteArrayList<>();list.add("A");list.add("B");Iterator<String> iterator = list.iterator();list.remove(0);  // 修改集合结构while (iterator.hasNext()) {    System.out.println(iterator.next());  // 不会抛出异常,继续遍历}



03
主要区别


特性fail-fastfail-safe
是否允许并发修改不允许并发修改,结构化修改会抛出异常允许并发修改,结构化修改不会抛出异常
抛出异常的方式发现并发修改时会快速抛出 ConcurrentModificationException 异常通过容忍并发修改或复制机制,不会抛出异常
适用场景单线程或修改频率较低的场景,适合小型应用并发修改较多的场景,适合高并发环境
性能较高,因为不需要额外的同步或复制机制较低,可能需要更多的内存和性能开销
实现基于检查修改计数,通常在遍历时进行检查通过复制机制(如写时复制)来避免问题


04
何时选择 fail-fast 和 fail-safe


使用 fail-fast:

  • 当你想要确保在程序出现并发修改时能够尽早发现问题时。它会帮助你避免意外的逻辑错误。

  • 适用于并发修改较少或者你能确保集合的修改是在合适的时机进行的场景。

使用 fail-safe:

  • 当你在并发环境下操作集合时,且不希望修改操作影响遍历时,选择 fail-safe。

  • 适用于高并发、线程安全的场景,例如多个线程修改集合的同时,你仍然希望能够安全地遍历集合。


05
总结


  • fail-fast 集合在发生并发修改时会快速抛出异常,通常适用于并发修改较少的场景,并且可以帮助发现错误。

  • fail-safe 集合则通过某种机制(如副本)避免抛出异常,即使在并发修改的情况下也能安全迭代,适用于高并发环境,但可能带来性能开销。

今天的内容就分享到这儿,喜欢的朋友可以关注,点赞。有什么不足的地方欢迎留言指出,您的关注是我前进的动力!

END


扫码关注

一起积累后端知识
不积跬步,无以至千里
不积小流,无以成江海

喜欢此内容的人还喜欢

谈谈id那些事(五)——美团的 Leaf 的ID生成


一个阿里二面面试官必问的问题


谈谈id那些事(三)——阿里巴巴的 TDDL的ID生成


分享面试:mysql数据库索引失效的情况


面试常被忽略的问题——内存区域划分