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. Sequelize 关系模型简介

    Sequelize 关系模型简介 先介绍一下本文用到的术语: 源: 调用 sequelize 中关系方法的调用者 目标: 调用 sequelize 中关系方法中的参数 比如, User.hasOne( ...

  2. 使用ab进行压力测试

    在Windows系统的命令行下,进入ab.exe程序所在目录,执行ab.exe程序.注意直接双击无法正确运行.

  3. UITableView的创建及其一些常用方法

    UITableView,它的数据源继承于UITableViewDataSource,它的委托UITableViewDelegate. 一.UITableView的创建 1.代码方式: UITableV ...

  4. 2、C#面向对象:封装、继承、多态、String、集合、文件(上)

    面向对象封装 一.面向对象概念 面向过程:面向的是完成一件事情的过程,强调的是完成这件事情的动作. 面向对象:找个对象帮你完成这件事情. 二.面向对象封装 把方法进行封装,隐藏实现细节,外部直接调用. ...

  5. matlab clear

    clear 删除工作空间中的项目,释放系统内存 语法: clear clear name clear name1 name2 name3... clear global name clear -reg ...

  6. 35、重新复习html与css(1)

    1.html与css的结合方式 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "ht ...

  7. python基础知识---数据结构之间的转换

  8. Android应用开发项目结构分析

    初学Android开发,初步理解的Android应用项目结构,备忘. 一.清单文件AndroidManifest.xml 功能: 1.供Android平台调用,供其了解本应用的信息,因此,所有的组件( ...

  9. ConCurrent in Practice小记 (3)

    ConCurrent in Practice小记 (3) 高级同步技巧 Semaphore Semaphore信号量,据说是Dijkstra大神发明的.内部维护一个许可集(Permits Set),用 ...

  10. web前端相关网站

    传智播客.               (有很多公开课的,而且他的百度云盘也有很多东西)               http://www.itcast.cn/(这个不是广告,爱信不信) 慕课网   ...