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分析的更多相关文章

  1. Java并发集合(一)-CopyOnWriteArrayList分析与使用

    CopyOnWriteArrayList分析与使用 原文链接: http://ifeve.com/java-copy-on-write/ 一.Copy-On-Write Copy-On-Write简称 ...

  2. CopyOnWriteArrayList分析——能解决什么问题

    CopyOnWriteArrayList主要可以解决的问题是并发遍历读取无锁(通过Iterator) 对比CopyOnWriteArrayList和ArrayList 假如我们频繁的读取一个可能会变化 ...

  3. Java CopyOnWriteArrayList分析

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

  4. java基础解析系列(八)---fail-fast机制及CopyOnWriteArrayList的原理

    fail-fast机制及CopyOnWriteArrayList的原理 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列( ...

  5. ArrayList的ConcurrentModificationException异常和多线程下的异常

    一.ConcurrentModificationException ArrayList源码看为什么出现异常: public class ArrayList<e> extends Abstr ...

  6. 多线程JUC并发篇常见面试详解

    @ 目录 1.JUC 简介 2.线程和进程 3.并非与并行 4.线程的状态 5.wait/sleep的区别 6.Lock 锁(重点) 1.Lock锁 2.公平非公平: 3.ReentrantLock ...

  7. 【JUC】JDK1.8源码分析之CopyOnWriteArrayList(六)

    一.前言 由于Deque与Queue有很大的相似性,Deque为双端队列,队列头部和尾部都可以进行入队列和出队列的操作,所以不再介绍Deque,感兴趣的读者可以自行阅读源码,相信偶了Queue源码的分 ...

  8. jdk并发包 CopyOnWriteArrayList源代码分析

    CopyOnWriteArrayList是jdk1.5并法包里面用于处理高并发下.读多写少的情况下.减少锁等待的集合类.以下对该类实现做一个简要的分析 1,首先CopyOnWriteArrayList ...

  9. CopyOnWriteArrayList实现原理及源码分析

    CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全且读操作无锁的ArrayList,写操作则通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可 ...

随机推荐

  1. 【转】spring MVC入门示例(hello world demo)

    部分内容来自网络:<第二章 Spring MVC入门 —— 跟开涛学SpringMVC > 1. Spring MVC介绍 Spring Web MVC是一种基于Java的实现了Web M ...

  2. 【Python 数据分析】从Mysql数据库取出数据作图分析

    在之前的文章中[爬取天气信息]我们已经将昆明二月份的气温爬取到数据库了,那么现在我们需要对这些数据进行一些分析操作,下面是使用matplotlib对这些数据的一些操作 折线图 首先我们读取数据库中的数 ...

  3. 10-hibernate单表操作-组件属性

    组件属性: 实体类中某个属性属于用户自定义的类的对象,比如在实体类中某个属性是自定义类的对象: 这个Address是一个用户自定义类. 该自定义类Address定义如下: //地址类 public c ...

  4. 基于vue单页应用的例子

    代码地址如下:http://www.demodashi.com/demo/13374.html 目录结构 src目录 主要的代码目录 components 存放项目组件 router 路由文件 sto ...

  5. TCP/IP协议族-----21、文件传送:FTP和TFTP

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGVrZXdhbmd6aQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...

  6. js 时间毫秒

    1. "2014-08-18 00:00:00"  与 13位毫秒 互换 var oTime = { _format_13_time:function (str){ var tim ...

  7. jqgrid 加按钮列

    1.在jqgrid表格中增加列,内容是图标,定义图标单击事件,可以操作这一行的数据,如下图 2.前台代码 <div id="grid_List"> <table ...

  8. python selenium ---键盘事件

    转自:http://www.cnblogs.com/fnng/p/3258946.html 本节重点: l 键盘按键用法 l 键盘组合键用法 l send_keys() 输入中文运行报错问题 键盘按键 ...

  9. android 屏幕适配原则

    屏幕大小 1.不同的layout Android手 机屏幕大小不一,有480x320,640x360,800x480.怎样才能让App自动适应不同的屏幕呢? 其实很简单,只需要在res目录下创建不同的 ...

  10. 3.3v转5v开关电源芯片LM2731

    方案一 输入3.3(可为2.7~14v):输出5v,700ma.已经过实际验证. 其中:C1,C2为贴片陶瓷电容,Cf也为贴片陶瓷电容,L1为6.8uH 电感 输出值只和R1,R2的值有关,但手册中又 ...