Java CopyOnWriteArrayList
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的更多相关文章
- java CopyOnWriteArrayList的使用
除了加锁外,其实还有一种方式可以防止并发修改异常,这就是将读写分离技术(不是数据库上的). 先回顾一下一个常识: 1.JAVA中“=”操作只是将引用和某个对象关联,假如同时有一个线程将引用指向另外一个 ...
- Java CopyOnWriteArrayList分析
CopyOnWriteArrayList是一种线程安全的ArrayList,顾名思义,它会利用写时拷贝技术,它对共享对象做仅仅读操作的时候,大家都用一个共享对象,假设有可变的操作时,就会复制一份出来, ...
- java.util.ConcurrentModificationException 多线程访问ArrayList引起
http://blog.csdn.net/androiddevelop/article/details/21509345 Java ConcurrentModificationException ...
- JAVA Concurrent包 中的并发集合类
我们平时写程序需要经常用到集合类,比如ArrayList.HashMap等,但是这些集合不能够实现并发运行机制,这样在服务器上运行时就会非常的消耗资源和浪费时间,并且对这些集合进行迭代的过程中不能进行 ...
- HowToDoInJava Java 教程·翻译完成
原文:HowToDoInJava 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远. ApacheCN 学习资源 目录 核心 Java 教程 什 ...
- Spark案例分析
一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...
- JAVA 多线程随笔 (三) 多线程用到的并发容器 (ConcurrentHashMap,CopyOnWriteArrayList, CopyOnWriteArraySet)
1.引言 在多线程的环境中,如果想要使用容器类,就需要注意所使用的容器类是否是线程安全的.在最早开始,人们一般都在使用同步容器(Vector,HashTable),其基本的原理,就是针对容器的每一个操 ...
- Java多线程系列--“JUC集合”02之 CopyOnWriteArrayList
概要 本章是"JUC系列"的CopyOnWriteArrayList篇.接下来,会先对CopyOnWriteArrayList进行基本介绍,然后再说明它的原理,接着通过代码去分析, ...
- java并发编程:并发容器之CopyOnWriteArrayList(转)
原文:http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开大家都在共享同一个内容,当某个 ...
随机推荐
- SpringBoot的基础Pom
原文连接 http://www.ibm.com/developerworks/cn/java/j-lo-spring-boot/ 名称 说明 spring-boot-starter 核心 POM,包含 ...
- Nancy+BUI+SQLite自动更新服务端和客户端保护更新程序
写了一个自动更新服务网站和自动更新客户端.基本功能测试完毕.记录下 项目地址:https://github.com/TeemoHQ/AutoUpdate 需求 自动集成,回退版本,守护进程,服务端操作 ...
- Linux内核分析总结
张潇月 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 学习目录: (1)计算机是如何工作的 h ...
- JMeter 聚合报告之 90% Line 参数说明
其实要说明这个参数的含义非常简单,可能你早就知道他的含义,但我对这个参数一直有误解,而且还一直以为是"真理",原于一次面试,被问到了这个问题,所以引起我这个参数的重新认识. 先说说 ...
- PHP常用函数大全
usleep() 函数延迟代码执行若干微秒.unpack() 函数从二进制字符串对数据进行解包.uniqid() 函数基于以微秒计的当前时间,生成一个唯一的 ID.time_sleep_until() ...
- nginx缓存引发的问题
请求为f.chinasoft.com/file f.chinasoft.com 域名指向slb(3.3.3.3) 业务方式: ios-->slb(3.3.3.3)-->ecs集群(每一台e ...
- ffmpeg基础
背景知识ffmpeg是一款领先的流媒体处理框架,支持编码,解码,转码等功能并可以在linux, Mac OS X, Microsoft Windows编译运行,用它做播放器的有:ffplay,射手播放 ...
- Linux常用命令1
jps(Java Virtual Machine Process Status Tool)是JDK 1.5提供的一个显示当前所有Java进程pid的命令. jps [ options ] [ host ...
- Google V8编程详解(二)HelloWorld
转自http://blog.csdn.net/feiyinzilgd/article/details/8248448 上一章讲到了V8的编译和安装,这一章开始从一个demo着手. 这里选用了官方文档的 ...
- 微信支付-“申请退款”接口遇到curl出错,错误码:58
该错误是没有 使用证书 导致的. 解决办法: 1. 下载证书. 2. 拷贝到 WxPayPubHelper/cacert/目录下 重试退款,OK