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. 在LoadRunner中查找和替换字符串

    参考<Search & Replace function for LoadRunner>: http://ptfrontline.wordpress.com/2009/03/13/ ...

  2. RAC环境下的堵塞(blocking blocked)

    RAC环境下的堵塞不同于单实例情形,由于我们须要考虑到位于不同实例的session.也就是说之前查询的v$session,v$lock对应的应变化为全局范围来查找.本文提供了2个查询脚本,并给出实例演 ...

  3. 基于redis的简易分布式爬虫框架

    代码地址如下:http://www.demodashi.com/demo/13338.html 开发环境 Python 3.6 Requests Redis 3.2.100 Pycharm(非必需,但 ...

  4. css定位positon

    值 描述 absolute 生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位. 元素的位置通过 "left", "top", " ...

  5. PHP 抽象类的使用

    //抽象类就是一个模版 abstract class db{ /* 参数:sql语句 返回值:索引的数组 */ abstract public function test($str); //没有方法体 ...

  6. POJ2185-Milking Grid(KMP,next数组的应用)

    Milking Grid Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 6317   Accepted: 2648 Desc ...

  7. Effective JavaScript Item 37 认识this的隐式指向

    本系列作为Effective JavaScript的读书笔记. CSV数据通常都会被某种分隔符进行分隔.所以在实现CSV Reader时,须要支持不同的分隔符.那么,非常自然的一种实现就是将分隔符作为 ...

  8. Yii2中对数据库的查询方法如下

    User::find()->all(); 此方法返回所有数据: User::findOne($id); 此方法返回 主键 id=1 的一条数据(举个例子): User::find()->w ...

  9. 解决国内gem不能用的问题

    转自:http://www.haorooms.com/post/gem_not_use 最近在安装SASS的时候,用到gem命令,但是运行出行如下错误! C:\Users\len>gem ins ...

  10. CentOS6.2下Qt5.1.0无法输入中文

    因为在程序中需要在界面上输入中文,但是系统是英文系统,没有预装中文输入法,于是从网上搜了一下输入法的安装,但是输入法安装好之后,可以再系统中输入中文,但是却无法再Qt中输入中文,只能继续找解决办法 安 ...