Java - Java集合中的安全失败Fail Safe机制 (CopyOnWriteArrayList)
文章目录
- Pre
- 概述
- fail-safe的容器—CopyOnWriteArrayList
- add
- remove函数
- 例子
- 缺陷
- 使用场景
Pre
Java - Java集合中的快速失败Fail Fast 机制
概述
ArrayList使用fail-fast机制自然是因为它增强了数据的安全性。
但在某些场景,我们可能想避免fail-fast机制抛出的异常,这时我们就要将ArrayList
替换为使用fail-safe机制的CopyOnWriteArrayList
.
采用安全失败机制的集合容器,在 Iterator 的实现上没有设计抛出 ConcurrentModificationException
的代码段,从而避免了fail-fast。
fail-safe的容器—CopyOnWriteArrayList
写时复制: 当我们往一个容器添加元素的时候,先将当前容器复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。
好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
add
public boolean add(E e) {// 可重入锁final ReentrantLock lock = this.lock;// 获取锁lock.lock();try {// 元素数组Object[] elements = getArray();// 数组长度int len = elements.length;// 复制数组Object[] newElements = Arrays.copyOf(elements, len + 1);// 存放元素enewElements[len] = e;// 设置数组setArray(newElements);return true;} finally {// 释放锁lock.unlock();}
}
此函数用于将指定元素添加到此列表的尾部,处理流程如下
- 获取锁(保证多线程的安全访问),获取当前的Object数组,获取Object数组的长度为length,进入步骤②。
- 根据Object数组复制一个长度为length+1的Object数组为newElements(此时,newElements[length]为null),进入下一步骤。
- 将下标为length的数组元素newElements[length]设置为元素e,再设置当前Object[]为newElements,释放锁,返回。这样就完成了元素的添加。
remove函数
public E remove(int index) {// 可重入锁final ReentrantLock lock = this.lock;// 获取锁lock.lock();try {// 获取数组Object[] elements = getArray();// 数组长度int len = elements.length;// 获取旧值E oldValue = get(elements, index);// 需要移动的元素个数int numMoved = len - index - 1;if (numMoved == 0) // 移动个数为0// 复制后设置数组setArray(Arrays.copyOf(elements, len - 1));else { // 移动个数不为0// 新生数组Object[] newElements = new Object[len - 1];// 复制index索引之前的元素System.arraycopy(elements, 0, newElements, 0, index);// 复制index索引之后的元素System.arraycopy(elements, index + 1, newElements, index,numMoved);// 设置索引setArray(newElements);}// 返回旧值return oldValue;} finally {// 释放锁lock.unlock();}
}
①获取锁,获取数组elements,数组长度为length,获取索引的值elements[index],计算需要移动的元素个数(length - index - 1),若个数为0,则表示移除的是数组的最后一个元素,复制elements数组,复制长度为length-1,然后设置数组,进入步骤③;否则,进入步骤②
② 先复制index索引前的元素,再复制index索引后的元素,然后设置数组。
③ 释放锁,返回旧值
例子
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;class PutThread extends Thread {private CopyOnWriteArrayList<Integer> cowal;public PutThread(CopyOnWriteArrayList<Integer> cowal) {this.cowal = cowal;}public void run() {try {for (int i = 100; i < 110; i++) {cowal.add(i);Thread.sleep(50);}} catch (InterruptedException e) {e.printStackTrace();}}
}public class CopyOnWriteArrayListDemo {public static void main(String[] args) {CopyOnWriteArrayList<Integer> cowal = new CopyOnWriteArrayList<Integer>();for (int i = 0; i < 10; i++) {cowal.add(i);}PutThread p1 = new PutThread(cowal);p1.start();Iterator<Integer> iterator = cowal.iterator();while (iterator.hasNext()) {System.out.print(iterator.next() + " ");}System.out.println();try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}iterator = cowal.iterator();while (iterator.hasNext()) {System.out.print(iterator.next() + " ");}}
}
有一个PutThread线程会每隔50ms就向CopyOnWriteArrayList中添加一个元素,并且两次使用了迭代器,迭代器输出的内容都是生成迭代器时,CopyOnWriteArrayList的Object数组的快照的内容,在迭代的过程中,往CopyOnWriteArrayList中添加元素也不会抛出异常。
0 1 2 3 4 5 6 7 8 9 100
0 1 2 3 4 5 6 7 8 9 100 101 102 103
缺陷
由于写操作的时候,需要拷贝数组,会消耗内存,如果原数组的内容比较多的情况下,可能导致young gc或者full gc
不能用于实时读的场景,像拷贝数组、新增元素都需要时间,所以调用一个set操作后,读取到数据可能还是旧的,虽然CopyOnWriteArrayList 能做到最终一致性,但是还是没法满足实时性要求;
使用场景
合适读多写少的场景,不过这类慎用
谁也没法保证CopyOnWriteArrayList
到底要放置多少数据,万一数据稍微有点多,每次add/set都要重新复制数组,这个代价实在太高 ,容易引起故障
Java - Java集合中的安全失败Fail Safe机制 (CopyOnWriteArrayList)相关推荐
- Java - Java集合中的快速失败Fail Fast 机制
文章目录 什么是 fail-fast 源码解读 Itr 为什么对集合的结构进行修改会发生并发修改异常-源码分析 修改方法之 remove 修改方法之 add 案例分享 [案例一] [案例二] [案例三 ...
- java操作集合中 concurrentModifyException 异常的原因分析
java操作集合中 concurrentModifyException 异常的原因分析 参考文章: (1)java操作集合中 concurrentModifyException 异常的原因分析 (2) ...
- JAVA求集合中的组合
好几个月没弄代码了,今天弄个求组合的DEMO 思路是将集合的每个值对照一个索引,索引大小是集合的大小+2.索引默认为[000...000],当组合后选取的组合值demo为[0100..00].然后根据 ...
- Java 删除集合中指定的元素
使用 Collection 类的 collection.remove() 方法来删除集合中的指定的元素 完整代码 import java.util.*;public class Main {publi ...
- java遍历集合中的元素_java中如何遍历ArrayList集合中的元素并输出
问题: 假设集合ArrayList中存储的元素是整形数字1~5,遍历每个元素,将每个元素顺序输出. 在线学习视频推荐:java教学视频 示例如下:package work10; import java ...
- java List集合中contains方法总是返回false
ArrayList的contains方法 java 今天在用ArrayList类的caontains方法是遇到了问题,我写了一个存放User类的ArrayList 但在调用list.contains( ...
- java在集合中的方法变动的类_java中级面试题 之基础篇
一.Java基础 1. 实例方法和静态方法有什么不一样? 1.在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式.而实例方 ...
- java 来自集合中泛型序列化的bug,难得一遇
背景 jdk1.8 本次事故简介 报错定位 entity 数据库字段 调试断点 图片看完,我们发现本应该是List的地方竟然存着ArrayList. 排查逻辑1:此处属于查询完数据库后的事故 排查逻辑 ...
- netty心跳过程中 发送消息失败_netty心跳机制和断线重连(四)
心跳是为了保证客户端和服务端的通信可用.因为各种原因客户端和服务端不能及时响应和接收信息.比如网络断开,停电 或者是客户端/服务端 高负载. 所以每隔一段时间 客户端发送心跳包到客户端 服务端做出心 ...
最新文章
- linux rules.d文件
- inet_ntop php,inet_ntop()
- redis超时问题分析
- 整合swagger2生成Restful Api接口文档
- JavaScript学习笔记之数组(二)
- Web Service 一些对外公开的网络服务接口以及http://www.webxml.com.cn/zh_cn/index.aspx
- C# XML反序列化与序列化举例:XmlSerializer(转)
- Python版——博客网站四 编写日志创建页
- ecshop二次开发必备--数据库说明2
- 静态链表相关算法学习
- 开源医学图像处理平台NiftyNet简介
- 用U盘制作Windows7安装以及MacBook Air上装Win7
- 空间矢量脉冲宽度调制(SVPWM)Simulink仿真教程
- Redis入门学习笔记--附Redis工具类
- 实模式8086 与 保护模式80286
- 定性与定量的单变量正态性检验
- 无线路由器和有线路由器桥接
- beego框架:static目录下的apk文件浏览器下载使用正常,手机浏览器下载无法解析安装
- java使用httpclient简单模拟登陆微信公众开放平台
- Excel根据某列的值,用不同颜色区分数据行
热门文章
- lua和python哪个简单_盘点一下lua脚本和python的区别(基础)
- 如何启用用计算机iis,win7系统如何开启iis功能?电脑iis功能启用图文步骤教程...
- 监听对象中某一项的值_Vue中watch的详细用法
- C++引用入门教程(一)
- leetcode 组合总和
- tableau实战系列(一)-轻松升级你的 Tableau Server
- java 泛型接口 范型类 范型方法_泛型类、泛型方法、泛型接口
- LeetCode题组:第1162题-地图分析
- 【Python刷题】_3
- LeetCode-剑指 Offer 25. 合并两个排序的链表