java.util.concurrent在相应的并发集合的包中定义的通用集合类,为了有效地处理并发场景。间CopyOnWriteArrayList它是合适ArrayList。顾名思义CopyOnWrite,当写副本,在这里写下包含集合改变操作,将创建一个副本。

CopyOnWriteArrayList的实现

类的定义

public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable

能够看到没有继承不论什么子类,实现接口和ArrayList类似。

关键属性

/** The lock protecting all mutators */
transient final ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private volatile transient Object[] array;

相同是採用数组方式实现,多了一个volatile声明,用于保证线程可见性。没有size声明表示实际包括元素的大小。多了一个ReentrantLock对象声明。

常见方法

构造方法

public CopyOnWriteArrayList() {
setArray(new Object[0]); //默认创建一个空数组
}
public CopyOnWriteArrayList(Collection<? extends E> c) {
Object[] elements = c.toArray();
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elements.getClass() != Object[].class)
elements = Arrays.copyOf(elements, elements.length, Object[].class);//拷贝一份数组
setArray(elements);
}

size方法,直接返回数组大小,说明array数组仅仅包括实际大小的空间

public int size() {
return getArray().length;
}

get方法,和ArrayList中类似,只是没有index的范围推断

public E get(int index) {
return (E)(getArray()[index]);
}

add方法,能够看到不管是在尾部还是指定位置加入。都有锁定和解锁操作。在设置值之前都先将原先数组拷贝一份并扩容至size+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);//拷贝array属性,并扩展为length+1大小
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock(); //解锁
}
} public void add(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
Object[] newElements;
int numMoved = len - index;
if (numMoved == 0) //尾部加入
newElements = Arrays.copyOf(elements, len + 1);
else {
newElements = new Object[len + 1];
//elements[0,index) ---> newElements[0,index)
System.arraycopy(elements, 0, newElements, 0, index);
//elements[index,len) --> newElements[index+1,len+1)
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
newElements[index] = element;
setArray(newElements);
} finally {
lock.unlock();
}
}

set方法,ArrayList中set方法直接改变数组中相应的引用,这里须要拷贝数组然后再设置。

(else那个分支没看懂,为什么值没有改变还须要设置来保证volatile写语义)

public E set(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
Object oldValue = elements[index];
if (oldValue != element) {
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len);
newElements[index] = element;
setArray(newElements);
} else {
// Not quite a no-op; ensures volatile write semantics
setArray(elements);
}
return (E)oldValue;
} finally {
lock.unlock();
}
}

remove(int)方法,和指定位置加入类似,须要拷贝[0,index)和[index+1,len)之间的元素

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;nt
if (numMoved == 0) //删除最后一个元素
setArray(Arrays.copyOf(elements, len - 1));
else {
Object[] newElements = new Object[len - 1];
//elements[0,index) --> newElements[0,index)
System.arraycopy(elements, 0, newElements, 0, index);
//elements[index+1,len) --> newElements[index,len-1)
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
setArray(newElements);
}
return (E)oldValue;
} finally {
lock.unlock();
}
}

remove(Object)方法,分配一个len-1大小的新数组。遍历原来数组,假设找到则将原来数组以后的元素复制到新数组中并将list设置为新数组,否则直接给新数组赋值上原来数组。

public boolean remove(Object o) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (len != 0) {
// Copy while searching for element to remove
// This wins in the normal case of element being present
int newlen = len - 1;
Object[] newElements = new Object[newlen]; for (int i = 0; i < newlen; ++i) {
if (eq(o, elements[i])) {
// found one; copy remaining and exit
for (int k = i + 1; k < len; ++k)
newElements[k-1] = elements[k];
setArray(newElements);
return true;
} else
newElements[i] = elements[i];
} // special handling for last cell
if (eq(o, elements[newlen])) {
setArray(newElements);
return true;
}
}
return false;
} finally {
lock.unlock();
}
}

迭代器的实现

ArrayList中迭代器支持fast fail,一旦检測到遍历过程中发送了改动则会抛出ConcurrentModificationException;CopyOnWriteArrayList的迭代器因为改动的时候都会又一次copy一份数组,因此不存在并发改动问题。也不会抛出ConcurrentModificationException。

相同支持单向和双向迭代器,其iterator和listIterator方法都是通过内部类COWIterator创建。仅仅是前者返回接口限定为单向迭代Iterator<E>。

COWIterator定义

/** Snapshot of the array **/
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */
private int cursor;

构造器

private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}

iterator和listIterator中会传递当前数组的引用和cursor(无參方法为0,有參数方法为相应值)

常见方法

public boolean hasNext() {
return cursor < snapshot.length;
}
public boolean hasPrevious() {
return cursor > 0;
}
public E next() {
if (! hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
}
public E previous() {
if (! hasPrevious())
throw new NoSuchElementException();
return (E) snapshot[--cursor];
}

另外其它add、remove和set改动容器的方法都没有实现,直接throw new UnsupportedOperationException();

总结

1. CopyOnWriteArrayList的迭代器保留一个运行底层基础数组的引用,这个数组当前位于迭代器的起始位置,因为基础数组不会被改动(改动都是复制一个新的数组),因此对其同步仅仅须要保证数组内容的可见性。多个线程能够同一时候对这个容器进行迭代。而不会彼此干扰或者与改动容器的线程互相干扰。

不会抛出CocurrentModificationException。而且返回元素与创建迭代器创建时的元素全然一致。不必考虑之后改动操作带来影响。

2. 每次改动容器都会复制底层数组,这须要一定开销,特别是容器规模较大。仅当迭代操作远远多于改动操作时,才应该使用CopyOnWriteArrayList。

版权声明:本文博主原创文章。博客,未经同意不得转载。

CopyOnWriteArrayList源代码阅读器的更多相关文章

  1. spring framework 4 源代码阅读器(1) --- 事前准备

    在你开始看代码.的第一件事要做的就是下载代码. 这里:https://github.com/spring-projects/spring-framework 下载完整的使用发现gradle建立管理工具 ...

  2. EnumMap源代码阅读器

    EnumMap是一个用于存放键值为enum类型的map.全部的键值必须来自一个单一的enum类型.EnumMap内部用数组表示效率更高. EnumMap维持键值的自然顺序(即枚举类型常量声明的顺序), ...

  3. Silverlight类百度文库在线文档阅读器

    百度文库阅读器是基于Flash的,用Silverlight其实也可以做. 我实现的在线阅读器可以应用于内网文档发布,在线阅览审批等.没有过多的堆积功能,专注于核心功能.主要有以下特性: 1. 基于XP ...

  4. 【转】Tomcat总体结构(Tomcat源代码阅读系列之二)

    本文是Tomcat源代码阅读系列的第二篇文章,我们在本系列的第一篇文章:在IntelliJ IDEA 和 Eclipse运行tomcat 7源代码一文中介绍了如何在intelliJ IDEA 和 Ec ...

  5. 淘宝数据库OceanBase SQL编译器部分 源代码阅读--Schema模式

    淘宝数据库OceanBase SQL编译器部分 源代码阅读--Schema模式 什么是Database,什么是Schema,什么是Table,什么是列,什么是行,什么是User?我们能够能够把Data ...

  6. Web版RSS阅读器(二)——使用dTree树形加载rss订阅分组列表

    在上一边博客<Web版RSS阅读器(一)——dom4j读取xml(opml)文件>中已经讲过如何读取rss订阅文件了.这次就把订阅的文件读取到页面上,使用树形结构进行加载显示. 不打算使用 ...

  7. Silverlight类百度文库在线文档阅读器(转)

    百度文库阅读器是基于Flash的,用Silverlight其实也可以做. 我实现的在线阅读器可以应用于内网文档发布,在线阅览审批等.没有过多的堆积功能,专注于核心功能.主要有以下特性: 1. 基于XP ...

  8. 【转】Tomcat源代码阅读系列

    在IntelliJ IDEA 和 Eclipse运行tomcat 7源代码(Tomcat源代码阅读系列之一) Tomcat总体结构(Tomcat源代码阅读系列之二) Tomcat启动过程(Tomcat ...

  9. Adobe阅读器漏洞(adobe_cooltype_sing)学习研究

    实验环境:Kali 2.0+Windows XP sp3+Adobe Reader 9.0.0 类别:缓冲区溢出 描述:这个漏洞针对Adobe阅读器9.3.4之前的版本,一个名为SING表对象中一个名 ...

随机推荐

  1. c++windows内核编程笔记day12 硬盘逻辑分区管理、文件管理、内存管理

    windows系统磁盘文件存储: 分区格式:NTFS / FAT32 GetSystemDirectory();//获取系统路径 GetWindowsDirectory();//获取windows路径 ...

  2. fedora 18 源码编译 android 4.0.1

    1.编译环境: 系统:fedora 18 KED 桌面  (Fedora-18-i686-Live-KDE.iso) 处理器:酷睿i5双核 内存: 4GB 硬盘:46GB java版本:java ve ...

  3. JavaEE session机制

    JavaEE session机制 Http协议: 在讲session之前,必须说下Http协议,HTTP是一个client和server端请求和应答的标准(TCP).由HTTPclient发起一个请求 ...

  4. [置顶] CentOS release 5.4 (Final)重置root密码(图文)

  5. 为什么国外程序员爱用苹果Mac电脑?(转)

    Mac 在国外很受欢迎,尤其是在 设计/web开发/IT 人员圈子里.普通用户喜欢 Mac 可以理解,毕竟 Mac 设计美观,简单好用,没有病毒.那么为什么专业人士也对 Mac 情有独钟呢?从个人使用 ...

  6. hdu1503(最长公共子序列)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1503 题意:由两个字符串构造出另一个字符串,该字符串包含前两个字符串(按字符顺序,但不一定连续),使该 ...

  7. NGUI使用教程(3) 使用外部图片制作Atlas(图集)

    在实际操作之前有几个概念先弄清一下 Atlas:图集,把美术给你提供的素材,用 NGUI 的 Atlas Maker 工具.合成一张图片(准确的说,还同一时候生成了prefab.mat ). Spri ...

  8. 什么是gulp?

    gulp初涉 1.什么是gulp? gulp是前端开发过程中一种基于流的代码构建工具,是自动化项目的构建利器:它不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成:使用 ...

  9. SE 2014年4月12日

    BGP基础实验 拓扑 步骤: 1. 完成基本的配置 2. 按照需求自治系统AS 100 全网运行OSPF 单区域 3. 完成BGP基本配置 [RT2]bgp 100 [RT2-bgp]peer 67. ...

  10. 一 手游开发工具cocos2d-x editor初识

    可学习的demo: 7个实战项目 flappybird(飞扬小鸟).popstar(消灭星星).fruitninja(水果忍者).2048(数度消除). moonwarriors(月亮战神).frui ...