谈谈Java集合中的fail-fast和fail-safe

目录

fail-fast快速失败

fail-safe安全失败


fail-fast快速失败和fail-safe安全失败都是Java集合框架中,迭代器用于处理在迭代期间集合被修改的两种不同策略。

fail-fast快速失败

快速失败是一种错误检测机制,用于检测在遍历集合时,集合是否被结构性修改(add、remove等操作,修改值不会触发),如果被修改就抛出ConcurrentModificationException异常,以防止集合状态不一致。

例如,线程A遍历List集合过程中,线程B对List的内容进行修改,就会抛出ConcurrentModificationException异常。

fail-fast的原理其实就是,集合内部维护了一个modCount变量,迭代器在遍历时内部会保存一个expectedModCount。集合在被遍历期间如果内容发生了结构性改变,modCount就会被修改,每当迭代器调用next、remove等方法时都会检查迭代器保存的expectedModCount与集合内部的modCount是否相等,如果相等就会继续遍历,如果不相等就会终止遍历,并抛出ConcurrentModificationException异常。

fail-fast并不能100%检测到所有的并发修改,其目标是尽最大努力去检测异常,并不能保证并发安全。

java.util包下非线程安全的集合都使用的fail-fast快速失败机制,例如ArrayList、HashMap等。

例如下列代码,在迭代过程中进行add操作,改变了modCount的值,也会抛出异常

java 复制代码
package test;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Test3 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String element = iterator.next();
            System.out.println(element);
            // 在迭代过程中修改集合
            if (element.equals("B")) {
                list.add("D"); 
            }
        }
    }
}

fail-safe安全失败

安全失败fail-safe允许集合在被遍历时被修改,不会抛出抛出异常。采用fail-safe机制的集合,在使用迭代器遍历时,通常遍历的是这个集合的副本,这样即使原集合被修改,迭代器也不会被影响。

它的原理其实就是在迭代器遍历时,遍历原集合的副本,因此任何对于原集合的修改都不会影响到迭代器正在遍历的集合副本,但迭代器也看不到对于该集合的最新修改。

那优点其实就是保证了遍历操作的安全性,缺点就是不能访问到修改后的内容,感受不到实时变化。

java.util.concurrent包下的容器都是采用的fail-safe安全失败机制,可以在多线程下并发使用,对集合进行并发修改。例如ConcurrentHashMap、CopyOnWriteArrayList等。

相关推荐
SL_staff1 分钟前
告别报表重复开发:JVS-BI 模板市场的参数化继承实践
java·数据分析·数据可视化
SL_staff1 小时前
从邮箱传文档到协同流重构:企业文档协作的系统性破局实践
java·开源·github
smallyoung3 小时前
告别N+1:Spring Boot 一键数据翻译框架体系
java·后端·架构
Bazingga5 小时前
Java16 record 完整拆解——语法、语义、限制、反射
java·后端
SL_staff6 小时前
规则联动:给工厂装上‘自动化神经反射弧’——JVS·物联网平台规则引擎实战教程
java·人工智能·物联网
骑着蜗牛撵大象3276 小时前
Linux 进程间通信之管道:从 pipe() 的内核语义到 FIFO 落地排错
android·java·linux·系统编程·fifo·管道·进程间通信
WJ.Polar6 小时前
Java封装、继承、多态
java·开发语言
ly76897 小时前
PostgreSQL 备份恢复策略:pg_basebackup、WAL 归档与 PITR 在企业 Java 系统中的落地
java·数据库·postgresql·pitr·pg_basebackup·wal 归档
摇滚侠7 小时前
Java 中如何表示负数,有符号和无符号的区别,补码的计算规则,char byte short int long 的范围
java·开发语言
weixin_307779137 小时前
C++代码实现MATLAB中的fitrtree函数功能
开发语言·c++·算法·matlab