1. 为什么需要 CopyOnWriteArrayList

ArrayList 的内部实现是一个数组, 并且是动态扩容的, 当插入数据时, 先判断数组是否需要扩容, 如果需要扩容, 则先扩容, 再插入数据, 也就说插入由三步组成

1) 检查是否需要扩容

2) 扩容/不扩容

3) 数据加入到数组

代码如下

    public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}

这里如果出现并发操作, 会有两个问题

1) 如果同时进行扩容, 则有可能出现连续进行两次扩容的问题, 而实际只需要一次

2) 如果同时对数组进行赋值, 则有可能第一个赋值元素被覆盖, 因为可能两个线程拿到的 size 是一样的, 他们都填到数组的同一个槽里

再看另一个 add 操作

    public void add(int index, E element) {
rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}

这种情况下, add 分为四步

1) 检查是否需要扩容

2) 扩容

3) 移动数据

4) 插入数据

如果此时有并发的读取和插入操作, 则有可能出现读取到的值为 null 的情况, 例如 list.get(3) 跟 list.add(3, "new") 同时发生, 本来 list.get(3) 应该拿到 "old" 或者 "new", 现在却拿到了 null, 这是因为在取值的过程中正好发生了移动数据, 但是数据又还没被插入到移动的空槽里

2. 如何解决这些问题?

一种最简单的方式是对 ArrayList 的所有行为全部加锁, 例如 Collections.synchronizedList(list) 方法, 他会包装 list, 并对所有操作加锁

但是这种方式会 block 所有操作, 读, 写 都是串行的, 会影响效率

3. CopyOnWriteArrayList 如何解决这些问题

cowlist 的写操作全都加锁, 并且在加锁后会将底层数组复制一份再进行写操作, 当写操作完成以后, 整个替换底层数组

1) 使用锁, 即解决了并发写的问题

2) 读操作不加锁, 效率更高, 读写不冲突

3) 写操作使用副本控制, 解决读操作会读到 null 问题, 因为底层数据不会出现有空槽的中间状态

Java CopyOnWriteArrayList的更多相关文章

  1. java CopyOnWriteArrayList的使用

    除了加锁外,其实还有一种方式可以防止并发修改异常,这就是将读写分离技术(不是数据库上的). 先回顾一下一个常识: 1.JAVA中“=”操作只是将引用和某个对象关联,假如同时有一个线程将引用指向另外一个 ...

  2. Java CopyOnWriteArrayList分析

    CopyOnWriteArrayList是一种线程安全的ArrayList,顾名思义,它会利用写时拷贝技术,它对共享对象做仅仅读操作的时候,大家都用一个共享对象,假设有可变的操作时,就会复制一份出来, ...

  3. java.util.ConcurrentModificationException 多线程访问ArrayList引起

    http://blog.csdn.net/androiddevelop/article/details/21509345   Java ConcurrentModificationException ...

  4. JAVA Concurrent包 中的并发集合类

    我们平时写程序需要经常用到集合类,比如ArrayList.HashMap等,但是这些集合不能够实现并发运行机制,这样在服务器上运行时就会非常的消耗资源和浪费时间,并且对这些集合进行迭代的过程中不能进行 ...

  5. HowToDoInJava Java 教程·翻译完成

    原文:HowToDoInJava 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远. ApacheCN 学习资源 目录 核心 Java 教程 什 ...

  6. Spark案例分析

    一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...

  7. JAVA 多线程随笔 (三) 多线程用到的并发容器 (ConcurrentHashMap,CopyOnWriteArrayList, CopyOnWriteArraySet)

    1.引言 在多线程的环境中,如果想要使用容器类,就需要注意所使用的容器类是否是线程安全的.在最早开始,人们一般都在使用同步容器(Vector,HashTable),其基本的原理,就是针对容器的每一个操 ...

  8. Java多线程系列--“JUC集合”02之 CopyOnWriteArrayList

    概要 本章是"JUC系列"的CopyOnWriteArrayList篇.接下来,会先对CopyOnWriteArrayList进行基本介绍,然后再说明它的原理,接着通过代码去分析, ...

  9. java并发编程:并发容器之CopyOnWriteArrayList(转)

    原文:http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开大家都在共享同一个内容,当某个 ...

随机推荐

  1. 网站压力测试工具webbench使用说明

    一.webbench简介        Webbench是有名的网站压力测试工具,它是由Lionbridge公司(http://www.lionbridge.com)开发.它的帮助文件和文档请到:ww ...

  2. kettle之mongodb数据同步

    需求: 1.源数据库新增一条记录,目标库同时新增一条记录: 2.源数据库修改一条记录,目标库同时修改该条记录: 示例用到三个Kettle组件 下面详细说下每个组件的配置 Source: 本示例连接的是 ...

  3. Mongodb集群搭建过程及常见错误

    Replica Sets MongoDB 支持在多个机器中通过异步复制达到故障转移和实现冗余.多机器中同一时刻只 有一台是用于写操作.正是由于这个情况,为 MongoDB 提供了数据一致性的保障.担当 ...

  4. AIDL小结

    AIDL : Android Interface Define Language(接口定义语言) Service中跨进程间通信利器.... 一般都会有Client端和Server端(Server端提供 ...

  5. 并发读写缓存实现机制(一):为什么ConcurrentHashMap可以这么快?

    大家都知道ConcurrentHashMap的并发读写速度很快,但为什么它会这么快?这主要归功于其内部数据结构和独特的hash运算以及分离锁的机制.做游戏性能很重要,为了提高数据的读写速度,方法之一就 ...

  6. 常量函数、常量引用参数、常量引用返回值[C++]

    1. 关于常量引用正像在C语言中使用指针一样,C++中通常使用引用 有一个函数... foo()并且这个函数返回一个引用...... & foo()...., 一个指向位图(Bitmap)的引 ...

  7. GPIB:永远不会被淘汰 (转载)

    发布时间:2014-07-02    来源:www.china-igbt.com 1994年5月出版的<测试与测量世界>中刊登了我冒险撰写的一篇名为<GPIB,时刻保持警惕>的 ...

  8. Xena测试仪的自动化

    Xena,Xena Networks公司的网络测试仪,也能覆盖以太网L2~L7层测试仪,但功能较简单,界面也很简洁,用起来比较直观方便. 1.Xena的自动化测试场景 测试PC上的AT框架--> ...

  9. C#图片上传服务器缩放存储功能

    项目需求上需要用户上传头像.本来是用第三方的插件的.但是很多都是收费的. 不过,我需要花这钱么?是不?所以网络上搜集了些资料.. 果然.这个世界 大神是很多很多的. 用了个免费插件. <scri ...

  10. js判断页面是pc打开还是手机打开

    <script type="text/javascript"> function browserRedirect() { var sUserAgent = naviga ...