CopyOnWriteArrayList源代码阅读器
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源代码阅读器的更多相关文章
- spring framework 4 源代码阅读器(1) --- 事前准备
在你开始看代码.的第一件事要做的就是下载代码. 这里:https://github.com/spring-projects/spring-framework 下载完整的使用发现gradle建立管理工具 ...
- EnumMap源代码阅读器
EnumMap是一个用于存放键值为enum类型的map.全部的键值必须来自一个单一的enum类型.EnumMap内部用数组表示效率更高. EnumMap维持键值的自然顺序(即枚举类型常量声明的顺序), ...
- Silverlight类百度文库在线文档阅读器
百度文库阅读器是基于Flash的,用Silverlight其实也可以做. 我实现的在线阅读器可以应用于内网文档发布,在线阅览审批等.没有过多的堆积功能,专注于核心功能.主要有以下特性: 1. 基于XP ...
- 【转】Tomcat总体结构(Tomcat源代码阅读系列之二)
本文是Tomcat源代码阅读系列的第二篇文章,我们在本系列的第一篇文章:在IntelliJ IDEA 和 Eclipse运行tomcat 7源代码一文中介绍了如何在intelliJ IDEA 和 Ec ...
- 淘宝数据库OceanBase SQL编译器部分 源代码阅读--Schema模式
淘宝数据库OceanBase SQL编译器部分 源代码阅读--Schema模式 什么是Database,什么是Schema,什么是Table,什么是列,什么是行,什么是User?我们能够能够把Data ...
- Web版RSS阅读器(二)——使用dTree树形加载rss订阅分组列表
在上一边博客<Web版RSS阅读器(一)——dom4j读取xml(opml)文件>中已经讲过如何读取rss订阅文件了.这次就把订阅的文件读取到页面上,使用树形结构进行加载显示. 不打算使用 ...
- Silverlight类百度文库在线文档阅读器(转)
百度文库阅读器是基于Flash的,用Silverlight其实也可以做. 我实现的在线阅读器可以应用于内网文档发布,在线阅览审批等.没有过多的堆积功能,专注于核心功能.主要有以下特性: 1. 基于XP ...
- 【转】Tomcat源代码阅读系列
在IntelliJ IDEA 和 Eclipse运行tomcat 7源代码(Tomcat源代码阅读系列之一) Tomcat总体结构(Tomcat源代码阅读系列之二) Tomcat启动过程(Tomcat ...
- Adobe阅读器漏洞(adobe_cooltype_sing)学习研究
实验环境:Kali 2.0+Windows XP sp3+Adobe Reader 9.0.0 类别:缓冲区溢出 描述:这个漏洞针对Adobe阅读器9.3.4之前的版本,一个名为SING表对象中一个名 ...
随机推荐
- DL动态载入框架技术文档
DL动态载入框架技术文档 DL技术交流群:215680213 1. Android apk动态载入机制的研究 2. Android apk动态载入机制的研究(二):资源载入和activity生命周期管 ...
- 利用linux BT5来破解无线 破解无线
下面是自己整理的详细步骤,版权小冯全部. 一.提前准备好bt5的ISO镜像文件.和虚拟机,提前把虚拟机安装好.然后进行安装bt5. 二.进入页面,点击statx.进入可视化界面. 三.进入主界面后.下 ...
- spring MVC 下载文件(转)
springle MVC中如何下载文件呢? 比struts2 下载文件简单得多 先看例子: @ResponseBody @RequestMapping(value = "/download& ...
- CC 3-Palindromes(manacher)
传送门:3-Palindromes 题意:求为回文串且能整除3且不前导0的子串个数. 分析:由 manacher算法O(N)可算出以i为坐标的最长为p[i]回文子串,且Si-k,Si-k+1..... ...
- Hasor:生命周期
首先引用Wiki的介绍一下Hasor: “Hasor是一款开源框架.它是为了解决企业模块化开发中复杂性而创建的.Hasor遵循简单的依赖.单一职责,在开发多模块企业项目中更加有调理.然而Has ...
- HDU 3217 Health(状压DP)
Problem Description Unfortunately YY gets ill, but he does not want to go to hospital. His girlfrien ...
- 【mysql】关于子查询的一个例子
假设表my_tbl包含三个字段a,b,c:现在需要查询表中列a的每个不同值下的列b为最小值的记录量. 比如表记录为: a b c 1 3 'cd' 2 3 'nhd' 1 5 'bg' ...
- BGP的状态机制
Idle 状态:即空闲状态,不接受任何BGP的连接,等待Start事件的产生,如果有start事件产生,若有start事件产生,系统开启ConnectRetry定时器,向邻居发起TCP连接,并将状态变 ...
- poj2486(树形dp)
题目链接:http://poj.org/problem?id=2486 题意:一颗树,n个点(1-n),n-1条边,每个点上有一个权值,求从1出发,走m步,最多能遍历到的权值. 分析:非常不错的树形d ...
- win7系统u盘安装过程
1.准备好带有启动项的U盘,并把镜像解压到里面去 2.插上u盘,开机长按del键进入bois设置界面 在boot页面 1.boot device priority->1st boot devic ...