首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >深入Java集合框架:解密List的Fail-Fast与Fail-Safe机制

深入Java集合框架:解密List的Fail-Fast与Fail-Safe机制

原创
作者头像
bug菌
发布2024-11-19 08:55:36
发布2024-11-19 08:55:36
4290
举报
文章被收录于专栏:滚雪球学Java滚雪球学Java

好事发生

  这里推荐一篇实用的文章:《Java中POM模块互相引用问题的解决方案》,作者:【喵手】。

  这篇文章作者主要讲述了在当开发大型Java项目时,通常会将项目拆分为多个模块,以便于代码管理和功能解耦。然而,模块之间的相互依赖有时会带来复杂的依赖关系,特别是两个模块互相引用时,如何在POM(Project Object Model)文件中正确配置依赖关系成为一个常见的难题。...借此好文安利给大家。


  OK,那本期正文即将拉开帷幕。

🏆本文收录于「滚雪球学Java」专栏中,这个专栏专为有志于提升Java技能的你打造,覆盖Java编程的方方面面,助你从零基础到掌握Java开发的精髓。赶紧关注,收藏,学习吧!

代码语言:java
复制
环境说明:Windows 10 + IntelliJ IDEA 2021.3.2 + Jdk 1.8

📖 前言

在 Java 开发中,集合框架(Collection Framework)是我们处理数据时的重要工具。而 List 作为最常用的集合之一,提供了丰富的功能和灵活的使用方式。但在 List 的并发修改时,我们常会遇到一些意想不到的问题,比如 ConcurrentModificationException 异常。今天我们就来深入探讨一下 Java List 集合中的 Fail-FastFail-Safe 机制,希望帮助大家理解并掌握集合在并发修改中的表现。

📝 摘要

本篇文章围绕 Java 集合框架中的 List 的并发修改机制展开,主要探讨 Fail-Fast 与 Fail-Safe 两种机制的原理与实现。通过源码解读、案例分析等方式帮助大家了解这两种机制的区别、优缺点及使用场景,避免在并发环境中出现不必要的问题。

📜 简介

Java 的集合类在进行并发操作时,可能会引发一些问题,尤其是在对集合进行迭代的过程中修改集合本身时。这些操作往往会导致 ConcurrentModificationException 异常,这是由于 Fail-Fast 机制所致。为解决这一问题,Java 引入了 Fail-Safe 机制,它通过不同的方式来避免并发修改异常。接下来,我们将深入了解这两种机制的工作原理。

🌐 概述

Fail-FastFail-Safe 是 Java 集合框架中用于处理并发修改的两种不同机制:

  • Fail-Fast:在检测到集合被修改时立即抛出异常。
  • Fail-Safe:允许集合在被迭代的过程中进行修改,不会抛出异常。

🔍 核心源码解读

Fail-Fast 实现原理

Fail-Fast 机制的实现依赖于集合的结构修改计数器,即 modCount。当我们在迭代的过程中检测到 modCount 的值发生变化时,即视为集合发生了并发修改,抛出 ConcurrentModificationException 异常。

ArrayList 为例,modCount 通过如下代码实现:

代码语言:java
复制
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    transient int modCount = 0;

    public E get(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        return elementData(index);
    }

    public E remove(int index) {
        modCount++;
        // 删除逻辑
    }
}

Fail-Safe 实现原理

Fail-Safe 机制通过创建集合的副本来进行迭代,因此不会影响原集合的内容。CopyOnWriteArrayList 是典型的 Fail-Safe 实现,它在写操作时复制一份新数组,迭代器遍历的是旧数组,避免了并发修改异常。

代码语言:java
复制
public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    private transient volatile Object[] array;

    public E get(int index) {
        return (E) getArray()[index];
    }

    public boolean add(E e) {
        synchronized (this) {
            Object[] newArray = Arrays.copyOf(array, array.length + 1);
            newArray[array.length] = e;
            array = newArray;
            return true;
        }
    }
}

💡 案例分析

Fail-Fast 案例

在 Fail-Fast 的场景中,对集合进行修改会导致 ConcurrentModificationException

代码语言:java
复制
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class FailFastExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");

        try {
            for (String item : list) {
                if (item.equals("B")) {
                    list.remove(item);  // 触发异常
                }
            }
        } catch (ConcurrentModificationException e) {
            System.out.println("Caught ConcurrentModificationException!");
        }
    }
}

在上述代码中,遍历过程中对集合进行了修改,导致触发 Fail-Fast 机制并抛出异常。

Fail-Safe 案例

使用 Fail-Safe 机制的 CopyOnWriteArrayList,则不会发生异常:

代码语言:java
复制
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;

public class FailSafeExample {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");

        for (String item : list) {
            if (item.equals("B")) {
                list.remove(item);  // 不会抛出异常
            }
        }

        System.out.println(list);
    }
}

CopyOnWriteArrayList 中,迭代时不会影响集合的结构,因此不会出现异常。

代码解析:

在本次的代码演示中,我将会深入剖析每句代码,详细阐述其背后的设计思想和实现逻辑。通过这样的讲解方式,我希望能够引导同学们逐步构建起对代码的深刻理解。我会先从代码的结构开始,逐步拆解每个模块的功能和作用,并指出关键的代码段,并解释它们是如何协同运行的。通过这样的讲解和实践相结合的方式,我相信每位同学都能够对代码有更深入的理解,并能够早日将其掌握,应用到自己的学习和工作中。

在这段代码中,使用了 CopyOnWriteArrayList,这是一个典型的 Fail-Safe 集合。当我们遍历并修改集合时,并不会抛出 ConcurrentModificationException 异常,这是由于 CopyOnWriteArrayList 在写操作时使用了副本机制来保证线程安全和 Fail-Safe 特性。

代码解析
代码语言:java
复制
import java.util.concurrent.CopyOnWriteArrayList;

public class FailSafeExample {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");

        for (String item : list) {
            if (item.equals("B")) {
                list.remove(item);  // 不会抛出异常
            }
        }

        System.out.println(list);
    }
}
具体解析
  • Fail-Safe 特性CopyOnWriteArrayList 的迭代器不直接引用原集合,而是对集合的快照进行遍历。因此,当 remove 方法在遍历中被调用时,它修改的是集合的副本,不会影响当前的迭代操作,从而避免了 ConcurrentModificationException 异常。
  • 线程安全CopyOnWriteArrayList 是线程安全的集合类。在写操作(如 addremove)时,它会复制一个新数组并修改该副本,然后将新的副本指向集合。因此,读取操作不会受到写操作的影响,这样可以确保线程安全性。
运行结果

当运行该代码时,list.remove("B") 不会抛出异常,最终的集合内容为 ["A", "C"]。输出结果如下:

代码语言:plaintext
复制
[A, C]

🎬 应用场景演示

  • Fail-Fast 适合在 单线程不允许并发修改 的场景使用,比如对数据一致性要求较高的场景。
  • Fail-Safe 更适合在 多线程环境 中,允许对集合进行并发操作而不会影响迭代过程。

📝 优缺点分析

  • Fail-Fast
    • 优点:通过立即抛出异常,避免了并发修改导致的数据不一致性。
    • 缺点:不适合在多线程环境中使用,会频繁触发异常。
  • Fail-Safe
    • 优点:可以在多线程环境下使用,避免并发修改异常。
    • 缺点:复制集合导致内存开销较大,不适合大量数据的场景。

🧩 类代码方法介绍及演示

ArrayList 的 Fail-Fast 实现

代码语言:java
复制
public class FailFastList<E> extends ArrayList<E> {
    public boolean remove(Object o) {
        modCount++;  // 修改结构计数器
        return super.remove(o);
    }
}

CopyOnWriteArrayList 的 Fail-Safe 实现

代码语言:java
复制
public class FailSafeList<E> extends CopyOnWriteArrayList<E> {
    public boolean add(E e) {
        synchronized (this) {
            Object[] newArray = Arrays.copyOf(array, array.length + 1);
            newArray[array.length] = e;
            array = newArray;
            return true;
        }
    }
}

🧪 测试用例

Fail-Fast 测试

代码语言:java
复制
public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("X");
    list.add("Y");

    for (String item : list) {
        list.remove("X");
    }
}

代码解析:

在本次的代码演示中,我将会深入剖析每句代码,详细阐述其背后的设计思想和实现逻辑。通过这样的讲解方式,我希望能够引导同学们逐步构建起对代码的深刻理解。我会先从代码的结构开始,逐步拆解每个模块的功能和作用,并指出关键的代码段,并解释它们是如何协同运行的。通过这样的讲解和实践相结合的方式,我相信每位同学都能够对代码有更深入的理解,并能够早日将其掌握,应用到自己的学习和工作中。

在这段代码中,我们使用了 ArrayList 集合,并在遍历时对集合进行了修改。这会导致并发修改异常 (ConcurrentModificationException) 的发生,这是因为 ArrayListFail-Fast 的,在遍历时检测到结构被修改时会立刻抛出异常。

代码解析
代码语言:java
复制
import java.util.ArrayList;
import java.util.List;

public class FailFastTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("X");
        list.add("Y");

        for (String item : list) {
            list.remove("X");  // 触发 ConcurrentModificationException
        }
    }
}
解析
  • Fail-Fast 特性ArrayList 的迭代器(以及大多数集合的迭代器)在遍历时会监控集合的结构修改。如果在遍历过程中检测到集合的结构发生了变化(比如 remove 操作),就会抛出 ConcurrentModificationException 异常,以防止数据不一致性。
  • modCount 机制ArrayList 中维护了一个 modCount 变量,每次结构发生修改时都会更新 modCount,迭代器在遍历时会检查这个值是否发生变化,从而决定是否抛出异常。
运行结果

在运行该代码后,当 for 循环中的 list.remove("X") 被执行时,会引发 ConcurrentModificationException 异常,程序终止。

解决方案
  1. 使用显式迭代器:使用 Iterator 提供的 remove 方法来删除元素。Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String item = iterator.next(); if ("X".equals(item)) { iterator.remove(); // 使用迭代器的 remove 方法,避免异常 } }
  2. 使用 CopyOnWriteArrayList:如果需要在多线程或并发修改的情况下使用集合,可以考虑使用 CopyOnWriteArrayList,它是 Fail-Safe 的,不会抛出异常。
注意事项
  • Fail-Fast 集合适合单线程场景,如果需要并发修改,请考虑其他集合(如 CopyOnWriteArrayListConcurrentHashMap)。
  • 选择合适的集合和修改方法,避免出现并发修改异常,提高代码的健壮性。

Fail-Safe 测试

代码语言:java
复制
public static void main(String[] args) {
    CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
    list.add("X");
    list.add("Y");

    for (String item : list) {
        list.remove("X");  // 不会触发异常
    }
}

代码解析:

在本次的代码演示中,我将会深入剖析每句代码,详细阐述其背后的设计思想和实现逻辑。通过这样的讲解方式,我希望能够引导同学们逐步构建起对代码的深刻理解。我会先从代码的结构开始,逐步拆解每个模块的功能和作用,并指出关键的代码段,并解释它们是如何协同运行的。通过这样的讲解和实践相结合的方式,我相信每位同学都能够对代码有更深入的理解,并能够早日将其掌握,应用到自己的学习和工作中。

在这段代码中,我们使用了 CopyOnWriteArrayList 集合,它是一种线程安全的、支持并发修改的列表。在遍历时对集合进行了修改,但并没有触发 ConcurrentModificationException 异常,这正是 CopyOnWriteArrayListFail-Safe特性。

代码解析
代码语言:java
复制
import java.util.concurrent.CopyOnWriteArrayList;

public class FailSafeTest {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        list.add("X");
        list.add("Y");

        for (String item : list) {
            list.remove("X");  // 不会触发异常
        }

        System.out.println("修改后的列表:" + list);
    }
}
解析
  • Fail-Safe 特性:在 CopyOnWriteArrayList 中,遍历操作不会直接访问原始集合,而是使用集合的快照。因此,即便在遍历过程中对集合进行修改,也不会触发异常。
  • 线程安全CopyOnWriteArrayList 在写操作(如 addremove)时会创建一个新的副本,旧的副本用于当前的遍历,新的副本包含修改后的数据。
运行结果

在运行该代码后,list.remove("X") 将会从集合中删除元素 "X",但不会引发任何异常,最终输出的列表会显示剩余的元素 ["Y"]

注意事项
  • CopyOnWriteArrayList 适合读多写少的场景,因为每次写操作都会生成新的副本,写操作开销较大。
  • 如果频繁进行写操作,CopyOnWriteArrayList 可能导致内存和性能消耗增加,因此需要根据场景合理选择集合类型。

🔍 测试结果预期与代码分析

在 Fail-Fast 场景中,测试会触发 ConcurrentModificationException,因为 ArrayList 检测到并发修改。而 Fail-Safe 场景中,CopyOnWriteArrayList 不会触发异常,因为迭代的是旧数据。

✨ 小结

Fail-Fast 和 Fail-Safe 是 Java 集合框架中非常重要的两种并发机制。在开发中,选择合适的机制可以避免并发异常,确保数据一致性。

🔚 总结与寄语

希望通过本文,你不仅掌握了 List 中的 Fail-Fast 和 Fail-Safe 机制,还能更好地选择和使用合适的集合类型。学习集合框架是一门细活,记住多观察、多实践!

☀️建议/推荐你

  无论你是计算机专业的学生,还是对编程有兴趣的小伙伴,都建议直接毫无顾忌的学习此专栏「滚雪球学Java」,bug菌郑重承诺,凡是学习此专栏的同学,均能获取到所需的知识和技能,全网最快速入门Java编程,就像滚雪球一样,越滚越大,指数级提升。

  码字不易,如果这篇文章对你有所帮助,帮忙给bug菌来个一键三连(关注、点赞、收藏) ,您的支持就是我坚持写作分享知识点传播技术的最大动力。   同时也推荐大家关注我的硬核公众号:「猿圈奇妙屋」 ;以第一手学习bug菌的首发干货,不仅能学习更多技术硬货,还可白嫖最新BAT大厂面试真题、4000G Pdf技术书籍、万份简历/PPT模板、技术文章Markdown文档等海量资料,你想要的我都有!

📣关于我

  我是bug菌,CSDN | 掘金 | 腾讯云 | 华为云 | 阿里云 | 51CTO | InfoQ 等社区博客专家,历届博客之星Top30,掘金年度人气作者Top40,51CTO年度博主Top12,掘金等平台签约作者,华为云 | 阿里云| 腾讯云等社区优质创作者,全网粉丝合计30w+ ;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板等海量资料。


--End

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 好事发生
  • 📖 前言
  • 📝 摘要
  • 📜 简介
  • 🌐 概述
  • 🔍 核心源码解读
    • Fail-Fast 实现原理
    • Fail-Safe 实现原理
  • 💡 案例分析
    • Fail-Fast 案例
    • Fail-Safe 案例
      • 代码解析
      • 具体解析
      • 运行结果
  • 🎬 应用场景演示
  • 📝 优缺点分析
  • 🧩 类代码方法介绍及演示
    • ArrayList 的 Fail-Fast 实现
    • CopyOnWriteArrayList 的 Fail-Safe 实现
  • 🧪 测试用例
    • Fail-Fast 测试
      • 代码解析
      • 解析
      • 运行结果
      • 解决方案
      • 注意事项
    • Fail-Safe 测试
      • 代码解析
      • 解析
      • 运行结果
      • 注意事项
  • 🔍 测试结果预期与代码分析
  • ✨ 小结
  • 🔚 总结与寄语
  • ☀️建议/推荐你
  • 📣关于我
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档