文章目录

  • 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 

缺陷

  1. 由于写操作的时候,需要拷贝数组,会消耗内存,如果原数组的内容比较多的情况下,可能导致young gc或者full gc

  2. 不能用于实时读的场景,像拷贝数组、新增元素都需要时间,所以调用一个set操作后,读取到数据可能还是旧的,虽然CopyOnWriteArrayList 能做到最终一致性,但是还是没法满足实时性要求;

使用场景

合适读多写少的场景,不过这类慎用

谁也没法保证CopyOnWriteArrayList 到底要放置多少数据,万一数据稍微有点多,每次add/set都要重新复制数组,这个代价实在太高 ,容易引起故障

Java - Java集合中的安全失败Fail Safe机制 (CopyOnWriteArrayList)相关推荐

  1. Java - Java集合中的快速失败Fail Fast 机制

    文章目录 什么是 fail-fast 源码解读 Itr 为什么对集合的结构进行修改会发生并发修改异常-源码分析 修改方法之 remove 修改方法之 add 案例分享 [案例一] [案例二] [案例三 ...

  2. java操作集合中 concurrentModifyException 异常的原因分析

    java操作集合中 concurrentModifyException 异常的原因分析 参考文章: (1)java操作集合中 concurrentModifyException 异常的原因分析 (2) ...

  3. JAVA求集合中的组合

    好几个月没弄代码了,今天弄个求组合的DEMO 思路是将集合的每个值对照一个索引,索引大小是集合的大小+2.索引默认为[000...000],当组合后选取的组合值demo为[0100..00].然后根据 ...

  4. Java 删除集合中指定的元素

    使用 Collection 类的 collection.remove() 方法来删除集合中的指定的元素 完整代码 import java.util.*;public class Main {publi ...

  5. java遍历集合中的元素_java中如何遍历ArrayList集合中的元素并输出

    问题: 假设集合ArrayList中存储的元素是整形数字1~5,遍历每个元素,将每个元素顺序输出. 在线学习视频推荐:java教学视频 示例如下:package work10; import java ...

  6. java List集合中contains方法总是返回false

    ArrayList的contains方法 java 今天在用ArrayList类的caontains方法是遇到了问题,我写了一个存放User类的ArrayList 但在调用list.contains( ...

  7. java在集合中的方法变动的类_java中级面试题 之基础篇

    一.Java基础 1. 实例方法和静态方法有什么不一样? 1.在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式.而实例方 ...

  8. java 来自集合中泛型序列化的bug,难得一遇

    背景 jdk1.8 本次事故简介 报错定位 entity 数据库字段 调试断点 图片看完,我们发现本应该是List的地方竟然存着ArrayList. 排查逻辑1:此处属于查询完数据库后的事故 排查逻辑 ...

  9. netty心跳过程中 发送消息失败_netty心跳机制和断线重连(四)

    心跳是为了保证客户端和服务端的通信可用.因为各种原因客户端和服务端不能及时响应和接收信息.比如网络断开,停电 或者是客户端/服务端 高负载. 所以每隔一段时间 客户端发送心跳包到客户端  服务端做出心 ...

最新文章

  1. linux rules.d文件
  2. inet_ntop php,inet_ntop()
  3. redis超时问题分析
  4. 整合swagger2生成Restful Api接口文档
  5. JavaScript学习笔记之数组(二)
  6. Web Service 一些对外公开的网络服务接口以及http://www.webxml.com.cn/zh_cn/index.aspx
  7. C# XML反序列化与序列化举例:XmlSerializer(转)
  8. Python版——博客网站四 编写日志创建页
  9. ecshop二次开发必备--数据库说明2
  10. 静态链表相关算法学习
  11. 开源医学图像处理平台NiftyNet简介
  12. 用U盘制作Windows7安装以及MacBook Air上装Win7
  13. 空间矢量脉冲宽度调制(SVPWM)Simulink仿真教程
  14. Redis入门学习笔记--附Redis工具类
  15. 实模式8086 与 保护模式80286
  16. 定性与定量的单变量正态性检验
  17. 无线路由器和有线路由器桥接
  18. beego框架:static目录下的apk文件浏览器下载使用正常,手机浏览器下载无法解析安装
  19. java使用httpclient简单模拟登陆微信公众开放平台
  20. Excel根据某列的值,用不同颜色区分数据行

热门文章

  1. lua和python哪个简单_盘点一下lua脚本和python的区别(基础)
  2. 如何启用用计算机iis,win7系统如何开启iis功能?电脑iis功能启用图文步骤教程...
  3. 监听对象中某一项的值_Vue中watch的详细用法
  4. C++引用入门教程(一)
  5. leetcode 组合总和
  6. tableau实战系列(一)-轻松升级你的 Tableau Server
  7. java 泛型接口 范型类 范型方法_泛型类、泛型方法、泛型接口
  8. LeetCode题组:第1162题-地图分析
  9. 【Python刷题】_3
  10. LeetCode-剑指 Offer 25. 合并两个排序的链表