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,写操作则通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可 ...
随机推荐
- C、C++中如何成功嵌入python
修改lib文件名称,拷贝修改C:\Python27\libs目录下原来的python27.lib为python27_d.lib 包含头文件在C:\Python27\include目录下 包含lib文件 ...
- RMQ 算法入门
1. 概述 RMQ(Range Minimum/Maximum Query).即区间最值查询,是指这样一个问题:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A ...
- Android Exception 13(Can't create handler inside thread that has not called Looper.prepare())
10-12 17:02:55.500: E/AndroidRuntime(28343): FATAL EXCEPTION: Timer-2 10-12 17:02:55.500: E/AndroidR ...
- list_entry(ptr, type, member)——知道结构体内某一成员变量地址,求结构体地址
#define list_entry(ptr, type, member) \ ((type *)(() -> member))) 解释: 1 在0这个地址看做有一个虚拟的type类型的变量,那 ...
- 用 WebSocket 实现一个简单的客服聊天系统
一 需求 一个多商家的电商系统,比如京东商城,不同商家之间的客服是不同的,所面对的用户也是不同的.要实现一个这样的客服聊天系统,那该系统就必须是一个支持多客服.客服一对多用户的聊天系统. 二 思路 使 ...
- Layui 弹出层组件——layer的模块化开发实例应用
Layui 弹出层组件——layer的模块化开发实例应用 1.首先在package.json中引入layer组件依赖 2.在源码中应用这个依赖 3.在源码中编写代码应用此组件 4.效果验证:点击日历上 ...
- php引用(&)变量引用,函数引用,对象引用和参数引用用法
php引用(&)变量引用,函数引用,对象引用和参数引用用法 php的引用(就是在变量或者函数.对象等前面加上&符号) 在PHP 中引用的意思是:不同的名字访问同一个变量内容.与C语 ...
- 网页中font-family的属性解析
web中文字体的选择 web应用程序因其跨平台性被广泛应用,但是也为web应用程序运行带来了复杂的运行环境,比如各个系统字体的区别以及中英文字体显示的区别. 字体分类 网页常用字体通常分为5类:ser ...
- FtpHelper类匿名获取FTP文件
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net ...
- python selenium --frame
本节知识点: 多层框架或窗口的定位: switch_to_frame() switch_to_window() 智能等待: implicitly_wait() 对于一个现代的web应用,经常会出现框架 ...