CopyOnWriteArrayList分析
ArrayList是比较常用的一个可变大小的数组集合,但是是不能同步的。如果多个线程同时访问一个ArrayList实例,其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。一般通过加锁对象进行同步操作来完成,如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法将该列表“包装”起来:
List list = Collections.synchronizedList(new ArrayList(...));
此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 或 add 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。
CopyOnWriteArrayList是ArrayList的一个线程安全的变体实现,即可在多线程并发环境中使用。而它的可变操作都是通过对ArrayList中存储的数组通过一次新的复制来实现的。 当对列表进行修改(add,remove等)操作时,先lock,然后copy一份,也就是先复制一份当前的数据存储结构,然后进行相应的操作。这也就让get操作从同步中释放出来。因为对数据的改变会在副本中进行,所以不需要同步,只是有可能独到脏数据,但这对于某些应用来说,问题不大,适合于少量写大量读取的情况。
CopyOnWriteArrayList使用
CopyOnWriteArrayList数据结构比较简单,举个例子介绍它的用法:
List<String> list =new CopyOnWriteArrayList<String>();
list.add("4");
list.add("5");
list.add("6");
list.remove(2);
内部实现
CopyOnWriteArrayList内部采用的存储结构是数组,且在操作时用ReentrantLock进行锁操作:
transient final ReentrantLock lock = new ReentrantLock();
private volatile transient Object[] array;
CopyOnWriteArrayList的构造函数
CopyOnWriteArrayList提供了一些构造函数,下面是两个典型的函数
public CopyOnWriteArrayList() {
setArray(new Object[0]);//采用长度为0的数组
}
public CopyOnWriteArrayList(E[] toCopyIn) {
setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}
Arrays.copyOf内部使用System.arraycopy函数拷贝数据,因为System,arraycopy函数是native的,效率比自己写的数组拷贝效率高。
CopyOnWriteArrayList的get函数
Get函数比较简单,直接返回指定位置的元素。
public E get(int index) {
return (E)(getArray()[index]);
}
CopyOnWriteArrayList的add函数
add函数添加之前,先加锁创建一个数组,数组长度在当前基础上加1,同时复制所有旧数组数据到新数组,然后在新数组的最后一个位置,添加新数据。然后将该新数组的引用赋予当前数组,最后解锁
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);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
CopyOnWriteArrayList的remove函数
remove函数跟add函数过程相似:先创建一个新的数组(长度为 原来长度-1),然后复制元素两边的数据元素, 然后将该新数组的引用赋予当前数组,整个过程也是有锁的。
public E remove(int index) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object oldValue = elements[index];
int numMoved = len - index - 1;
if (numMoved == 0)//
setArray(Arrays.copyOf(elements, len - 1));
else {
Object[] newElements = new Object[len - 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
setArray(newElements);
}
return (E)oldValue;
} finally {
lock.unlock();
}
}
比较ArrayList和CopyOnWriteArrayList
单线程:在元素较少的景象下,两个类的性能接近,当元素很多时,CopyOnWriteArrayList增长元素的操作会差一点。
多线程:跟着元素数量和线程数量的增长,CopyOnWriteArrayList在添加和删除元素的机能就会降落,并且比ArrayList机能低。但在查找元素时在元素数量和线程数量的增长的情况下性能比ArrayList好。
在读多写少的并发场景中,CopyOnWriteArrayList比ArrayList有着性能大的优势,是一个不错的选择。
CopyOnWriteArrayList分析的更多相关文章
- Java并发集合(一)-CopyOnWriteArrayList分析与使用
CopyOnWriteArrayList分析与使用 原文链接: http://ifeve.com/java-copy-on-write/ 一.Copy-On-Write Copy-On-Write简称 ...
- CopyOnWriteArrayList分析——能解决什么问题
CopyOnWriteArrayList主要可以解决的问题是并发遍历读取无锁(通过Iterator) 对比CopyOnWriteArrayList和ArrayList 假如我们频繁的读取一个可能会变化 ...
- Java CopyOnWriteArrayList分析
CopyOnWriteArrayList是一种线程安全的ArrayList,顾名思义,它会利用写时拷贝技术,它对共享对象做仅仅读操作的时候,大家都用一个共享对象,假设有可变的操作时,就会复制一份出来, ...
- java基础解析系列(八)---fail-fast机制及CopyOnWriteArrayList的原理
fail-fast机制及CopyOnWriteArrayList的原理 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列( ...
- ArrayList的ConcurrentModificationException异常和多线程下的异常
一.ConcurrentModificationException ArrayList源码看为什么出现异常: public class ArrayList<e> extends Abstr ...
- 多线程JUC并发篇常见面试详解
@ 目录 1.JUC 简介 2.线程和进程 3.并非与并行 4.线程的状态 5.wait/sleep的区别 6.Lock 锁(重点) 1.Lock锁 2.公平非公平: 3.ReentrantLock ...
- 【JUC】JDK1.8源码分析之CopyOnWriteArrayList(六)
一.前言 由于Deque与Queue有很大的相似性,Deque为双端队列,队列头部和尾部都可以进行入队列和出队列的操作,所以不再介绍Deque,感兴趣的读者可以自行阅读源码,相信偶了Queue源码的分 ...
- jdk并发包 CopyOnWriteArrayList源代码分析
CopyOnWriteArrayList是jdk1.5并法包里面用于处理高并发下.读多写少的情况下.减少锁等待的集合类.以下对该类实现做一个简要的分析 1,首先CopyOnWriteArrayList ...
- CopyOnWriteArrayList实现原理及源码分析
CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全且读操作无锁的ArrayList,写操作则通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可 ...
随机推荐
- Openerp约束句型
内容摘自:http://blog.csdn.net/sz_bdqn/article/details/8785483 _constraints _constraints可以灵活定义OpenERP对象的约 ...
- 调整 firefox 源代码查看器的字体
默认的 firefox 源代码查看器的字体很不好看,不适合阅读代码.想要修改,又没有发现入口.如何修改呢?实际上在选项里的字体就能改.方法如下: 打开选项页,在字体一栏点击高级,把等宽字体由“宋体”改 ...
- Android NDK学习记录(一)
一.NDK环境在Mac中部署 1.准备eclipse,android sdk安装包,android ndk安装包(http://dl.google.com/android/ndk/android-nd ...
- Java之内部类(1) - 为什么需要内部类
为什么需要内部类 一般来说,内部类继承自某个类或实现某个接口,内部类的代码操作创建它的外围类的对象.所以可以认为内部类提供了某种进入其外围类的窗口. 内部类必须要回答的一个问题是:如果只是需要一个对接 ...
- 【VBA编程】12.Workbook对象常用属性
[ActiveSheet属性] ActiveSheet属性用于返回一个对象,表示活动工作簿中或指定的窗口或工作簿中的活动工作表 [Colors] Colors属性是一个Variant类型的可读写属性, ...
- Hibernate继承类的实现
版权声明:本文为博主原创文章,如需转载请标注转载地址. 博客地址:http://www.cnblogs.com/caoyc/p/5603724.html 对于继承关系类的映射.比如在论坛中文章(Ar ...
- JavaScript入门:002—JS代码放置的位置
JavaScript在页面中使用,那么这些JS代码应该放在什么位置呢?以下来看一下. 一般来说有两种方式.写在界面上和使用.js文件.1.1界面上的Head部分能够直接放在head标签内,例如以下代码 ...
- struts2中标签
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"% ...
- 【前端】CSS
CSS介绍 CSS(Cascading Style Sheet,层叠样式表)定义如何显示HTML元素.l 当浏览器读到一个样式表,它就会按照这个样式表来对文档进行格式化(渲染). CSS语法 CSS实 ...
- 关于ie,火狐,谷歌浏览器滚动条的隐藏以及自定义样式
最近做了一个项目,要求各个浏览器统一滚动条的样式,不显示滚动条,但是不影响鼠标的滑动事件. 查了很多资料,ie和谷歌都是可以自定义滚动条样式的,但是ie只能改变颜色,并不能修改宽度,圆角之类的.谷歌就 ...