当前位置: 首页 > news >正文

ArrayList和CopyOnWriteArrayList

ArrayList

ArrayList 的保护机制

for(String str : list){
    if(str.equals("123")){
        list.remove(str);   //抛出异常
    }
}

这里的 foreach 语法糖实际上调用了 ArrayList 的迭代器类。如下:

如果在开始迭代的时候数组中有 5 个元素,但是在迭代中移除了一个元素,数组实际上还有 4 个元素,但是还是会遍历第五个元素,这就导致了下标越界错误,ArrayList 不允许这种异常发生。还有在多线程下场景下,一个线程遍历这个 ArrayList , 另一个线程移除数组中的某个元素,也会发生 ConcurrentModificationException 异常。

fail-Fast 机制

这个机制就是 fail-Fast 机制,快速失败系统,通常设计用于停止有缺陷的过程,这是一种理念,在进行系统设计时优先考虑异常情况,一旦发生异常,直接停止并上报。

public int divide(int divisor, int dividend){
    if (dividend == 0) {
        throw new RuntimeException("被除数不能为 0");    //这里就是 fail-fast 的体现
    }
    return divisor / dividend;
}

保护机制的实现原理

在 ArrayList 中有一个成员变量: modCount, 它是从 AbstractList 继承来的, modCount 记录数组每次写操作的次数。当像数组增加或移除一个元素,其值加 1,初始值为 0,在开始遍历的时候,会记录当下数组的 modCount 值为 expectedModCount,遍历每个元素时都会比较 modCountexpectedModCount 两个值,如果不同,就会抛出异常,代表着在遍历的时候修改了数组。如下:

怎么样才可以在循环中修改数组?

如果我们每次向数组中添加或删除元素时,同步修改 exceptedModCount 就不会抛出异常了,ArrayList 没有直接提供这种方法,而是把这种方法委托给迭代器了:

所以我们可以这样做:

Iterator <String> iterator = list.iterator();
while(iterator.hasNext()){
    String str = iterator.next();
    iterator.remove();   //正确做法
}

CopyOnWriteArrayList

CopyOnWriteArrayList 是 ArrayList 的线程安全版本,读取无锁,写时有锁。适用于 写少读多的场景,会有不一致的现象

实现原理

见名知意—— 写时复制, 当线程在数组上移除,添加元素时,先加锁,将原数组复制一份,然后基于副本操作,最后将已经更改的副本覆盖元数组,释放锁。

其内部有一个 ReentranLock 来控制锁的获取和释放。

先看一下内部结构图:

get 方法

可以看到get方法非常简单,直接获取内部数组第i个元素,没有其他加锁的操作

add 方法

一些其他方法都是这种套路,不再一一罗列。

存在的问题

利用了空间换时间的思想提高性能,因为在每一步的写操作都复制了一个副本,如果数组比较大,就会导致内存占用急剧增加,引发频繁 full GC,从而影响系统性能。

如何解决

我们可以利用 ReenTranLock 自定义一个线程安全的ArrayList, 分别定义一个 读锁和写锁,读写、写写 互斥。 读读不互斥。避免这种数组的拷贝

相关文章:

  • 开放标准如何破解企业数字化与可持续发展的困境:The Open Group引领生态系统架构创新
  • 探索人工智能的未来趋势
  • 【iOS】push和present的区别
  • Oracle数据库中的动态SQL(Dynamic SQL)
  • 炫酷HTML蜘蛛侠登录页面
  • Python_两个jpg图片文件名称互换
  • 【二】【SQL】去重表数据及分组聚合查询
  • dcat admin自定义操作按钮
  • 【virtual Box】功能速通:安装 Windows 和 Ubuntu
  • Unity接入SQLite (三):C#封装SQL命令
  • 【MATLAB】小波 MFE_SVM_LSTM 神经网络时序预测算法
  • 计算机体系架构初步入门
  • Java中的JDK动态代理
  • 基于Springboot实现留守儿童管理系统
  • go-zero 成长之路—微服务电商实战系列(六、条件查询)
  • 注意力机制以及实现
  • 物联网ARM开发-9STM32窗口看门狗
  • 【附源码】计算机毕业设计SSM软件缺陷管理系统
  • GVIM基础教程——vimscript编程初步(一)
  • 企业级低代码平台Jeecgboot3.4.2及3.4.3版本新功能介绍
  • BERT之后,NLP主要预训练模型演变梳理
  • 一文读懂C++20新特性之概念、约束(concept, constraint)
  • 【学姐面试宝典】前端基础篇Ⅴ——JS深浅拷贝、箭头函数、事件监听等
  • Hive入门详解操作
  • 包含全国所有省份、城市、县的一份json文件
  • SAP UI5 index.html 根节点的 css 类填充逻辑
  • 你应该知道的JavaScript操作对象方法总结
  • openCV实战项目--人脸考勤
  • FlexRAN BBU Pool 设计的思考:为什么常见的设计是三维度的,以及类比,机器理解
  • 每天五分钟机器学习:神经网络比逻辑回归强大的本质
  • 美国每月1-2千美元的工资怎么生活?
  • win10搭建spark3.1环境(超详细)