1. CopyOnWriteArrayList,写数组的拷贝,支持高效率并发且是线程安全的,读操作无锁的ArrayList。所有可变操作都是通过对底层数组进行一次新的复制来实现。
  2. CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。它不存在扩容的概念,每次写操作都要复制一个副本,在副本的基础上修改后改变Array引用。CopyOnWriteArrayList中写操作需要大面积复制数组,所以性能肯定很差
  3. 在迭代器上进行的元素更改操作(remove、set和add)不受支持。这些方法将抛出UnsupportedOperationException。

定义

CopyOnWriteArrayList跟ArrayList一样实现了List, RandomAccess, Cloneable, Serializable接口,但是没有继承AbstractList。

初始化时候新建一个容量为0的数组。

add(E e)方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public boolean add(E e) {
//获得锁,添加的时候首先进行锁定
final ReentrantLock lock = this.lock;
lock.lock();
try {
//获取当前数组
Object[] elements = getArray();
//获取当前数组的长度
int len = elements.length;
//这个是重点,创建新数组,容量为旧数组长度加1,将旧数组拷贝到新数组中
Object[] newElements = Arrays.copyOf(elements, len + 1);
//要添加的数据添加到新数组的末尾
newElements[len] = e;
//将数组引用指向新数组,完成了添加元素操作
setArray(newElements);
return true;
} finally {
//解锁
lock.unlock();
}
}

从上面来说,每次添加一个新元素都会长度加1,然后复制整个旧数组,由此可见对于写多的操作,效率肯定不会很好。所以CopyOnWriteArrayList适合读多写少的场景。

add(int index, E element)方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public void add(int index, E element) {
//同样也是先加锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
//获取旧数组
Object[] elements = getArray();
//获取旧数组长度
int len = elements.length;
//校验指定的index
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
Object[] newElements;
int numMoved = len - index;
if (numMoved == 0)//需要插入的位置正好等于数组长度,数组长度加1,旧数据拷贝到新数组
newElements = Arrays.copyOf(elements, len + 1);
else {
//新数组长度增加1
newElements = new Object[len + 1];
//分两次拷贝,第一次拷贝旧数组0到index处的到新数组0到index,第二次拷贝旧数组index到最后的数组到新数组index+1到最后
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
//index初插入数据
newElements[index] = element;
//新数组指向全局数组
setArray(newElements);
} finally {
//解锁
lock.unlock();
}
}

set(int index, E element)方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public E set(int index, E element) {
//修改元素之前首先加锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
//获取原来的数组
Object[] elements = getArray();
//index位置的元素
E oldValue = get(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 oldValue;
} finally {
//解锁
lock.unlock();
}
}

get(int index)方法

读的时候不加锁,代码如下:

1
2
3
public E get(int index) {
return get(getArray(), index);
}
1
2
3
private E get(Object[] a, int index) {
return (E) a[index];
}

remove()

remove方法不再过多介绍,看完add和set方法应该就能理解。

迭代

内部类COWIterator 实现了ListIterator接口。迭代的时候不能进行remove,add,set等方法,会抛异常。

迭代速度快,迭代时是迭代的数组快照。

1
2
/** Snapshot of the array */
private final Object[] snapshot;

源码分析

jdk1.7.0_71

1
2
3
4
5
6
//锁,保护所有存取器
transient final ReentrantLock lock = new ReentrantLock();
//保存数据的数组
private volatile transient Object[] array;
final Object[] getArray() {return array;}
final void setArray(Object[] a) {array = a;}

空构造,初始化一个长度为0的数组

1
2
3
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}

利用集合初始化一个CopyOnWriteArrayList

1
public CopyOnWriteArrayList(Collection<? extends E> c) {}

利用数组初始化一个CopyOnWriteArrayList

1
public CopyOnWriteArrayList(E[] toCopyIn) {}

size() 大小

1
public int size() {}

isEmpty()是否为空

1
public boolean isEmpty(){}

indexOf(Object o, Object[] elements,int index, int fence) 元素索引

1
private static int indexOf(Object o, Object[] elements,int index, int fence) {}

indexOf() 元素索引

1
public int indexOf(Object o){}

indexOf(E e, int index) 元素索引

1
public int indexOf(E e, int index) {}

lastIndexOf(Object o, Object[] elements, int index) 元素索引,最后一个

1
private static int lastIndexOf(Object o, Object[] elements, int index) {}

lastIndexOf(Object o) 元素索引,最后一个

1
public int indexOf(E e, int index) {}

lastIndexOf(E e, int index) 元素索引,最后一个

1
public int lastIndexOf(E e, int index) {}

contains(Object o) 是否包含元素

1
public boolean contains(Object o){}

clone() 浅拷贝

1
public Object clone() {}

toArray() 转换成数组

1
public Object[] toArray(){}

toArray(T a[]) 转换成指定类型的数组

1
public <T> T[] toArray(T a[]) {}

E get(int index)获取指定位置的元素

1
public E get(int index){}

set(int index, E element) 指定位置设置元素

写元素的时候,先获得锁,finall块中释放锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
E oldValue = get(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 oldValue;
} finally {
lock.unlock();
}
}

add(E e) 元素添加到末尾

1
public boolean add(E e) {}

add(int index, E element) 指定位置之后插入元素

1
public void add(int index, E element){}

remove(int index)删除指定位置的元素

1
public E remove(int index) {}

remove(Object o) 删除第一个匹配的元素

1
public boolean remove(Object o) {}

removeRange(int fromIndex, int toIndex) 删除指定区间的元素

1
private void removeRange(int fromIndex, int toIndex) {}

addIfAbsent(E e) 如果元素不存在就添加进list中

1
public boolean addIfAbsent(E e){}

c)是否包含全部" style="color: rgb(85, 85, 85); text-decoration: none; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(204, 204, 204); word-wrap: break-word; background-color: transparent;">containsAll(Collection<?> c)是否包含全部

1
public boolean containsAll(Collection<?> c){}

c) 移除全部包含在集合中的元素" style="color: rgb(85, 85, 85); text-decoration: none; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(204, 204, 204); word-wrap: break-word; background-color: transparent;">removeAll(Collection<?> c) 移除全部包含在集合中的元素

1
public boolean removeAll(Collection<?> c){}

c) 保留指定集合的元素,其他的删除" style="color: rgb(85, 85, 85); text-decoration: none; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(204, 204, 204); word-wrap: break-word; background-color: transparent;">retainAll(Collection<?> c) 保留指定集合的元素,其他的删除

1
public boolean retainAll(Collection<?> c){}

c) 如果不存在就添加进去" style="color: rgb(85, 85, 85); text-decoration: none; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(204, 204, 204); word-wrap: break-word; background-color: transparent;">addAllAbsent(Collection<? extends E> c) 如果不存在就添加进去

1
public int addAllAbsent(Collection<? extends E> c) {}

clear() 清空list

1
public void clear(){}

c)添加集合中的元素到尾部" style="color: rgb(85, 85, 85); text-decoration: none; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(204, 204, 204); word-wrap: break-word; background-color: transparent;">addAll(Collection<? extends E> c)添加集合中的元素到尾部

1
public void addAll(Collection<? extends E> c){}

c) 添加集合中元素到指定位置之后" style="color: rgb(85, 85, 85); text-decoration: none; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(204, 204, 204); word-wrap: break-word; background-color: transparent;">addAll(int index, Collection<? extends E> c) 添加集合中元素到指定位置之后

1
public boolean addAll(int index, Collection<? extends E> c){}

toString()

1
public String toString(){}

equals(Object o)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof List))
return false;

List<?> list = (List<?>)(o);
Iterator<?> it = list.iterator();
Object[] elements = getArray();
int len = elements.length;
for (int i = 0; i < len; ++i)
if (!it.hasNext() || !eq(elements[i], it.next()))
return false;
if (it.hasNext())
return false;
return true;
}

hashCode()

1
public int hashCode{}

listIterator(final int index)和 listIterator() 返回一个迭代器,支持向前和向后遍历

1
2
3
public ListIterator<E> listIterator(final int index) {}

public ListIterator<E> listIterator() {}

iterator() 只能向后遍历

1
public Iterator<E> iterator() {}

subList() 返回部分list

1
2
3
4
5
public List<E> subList(int fromIndex, int toIndex) {
...
return new COWSubList<E>(this, fromIndex, toIndex);
...
}

参考

http://www.cnblogs.com/sunwei2012/archive/2010/10/08/1845656.html

http://my.oschina.net/jielucky/blog/167198


CopyOnWriteArrayList简介的更多相关文章

  1. java多线程系列12 ConcurrentHashMap CopyOnWriteArrayList 简介

    我们知道 ,hashmap 和 arraylist 是线程不安全的 在多线程环境下有数据安全问题, 当然 我们可以通过Collections的一些方法把他们变成线程安全的, Collections.s ...

  2. J.U.C并发框架源码阅读(十五)CopyOnWriteArrayList

    基于版本jdk1.7.0_80 java.util.concurrent.CopyOnWriteArrayList 代码如下 /* * Copyright (c) 2003, 2011, Oracle ...

  3. java多线程系列 目录

    Java多线程系列1 线程创建以及状态切换    Java多线程系列2 线程常见方法介绍    Java多线程系列3 synchronized 关键词    Java多线程系列4 线程交互(wait和 ...

  4. java_guide_9-30_并发相关

    3.1 CopyOnWriteArrayList 简介 public class CopyOnWriteArrayList<E> extends Object implements Lis ...

  5. Java集合容器简介

    Java集合容器主要有以下几类: 1,内置容器:数组 2,list容器:Vetor,Stack,ArrayList,LinkedList, CopyOnWriteArrayList(1.5),Attr ...

  6. CompletionService 简介

    以下是jdk关于CompletionService的简介: public interface CompletionService<V> 将生产新的异步任务与使用已完成任务的结果分离开来的服 ...

  7. Java并发包中CopyOnWrite容器相关类简介

    简介: 本文是主要介绍,并发容器CopyOnWriteArrayList和CopyOnWriteArraySet(不含重复元素的并发容器)的基本原理和使用示例. 欢迎探讨,如有错误敬请指正 如需转载, ...

  8. 死磕 java集合之CopyOnWriteArrayList源码分析

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 简介 CopyOnWriteArrayList是ArrayList的线程安全版本,内部也是通过 ...

  9. JDBC驱动程序注册 JDBC简介(二)

    使用JDBC进行数据库操作的第一步就是驱动注册(当然你得先导入JAR). 驱动注册有多种方式,第一步必然是获得正确的驱动名称与URL格式 驱动名称与URL格式 RDBMS 驱动程序名称        ...

随机推荐

  1. rails常用命令备忘

    rails new xxx 创建一个新rails项目 rails generate scaffold xxx 创建表模型,视图,控制器和迁移的"脚手架" rake db:migra ...

  2. Salesforce的数据权限机制

    本文主要介绍了 Salesforce 对于系统中数据的访问控制是如何设计的,然后也了解了下 Alfresco 和 Oracle VPD 的数据权限机制.希望对一些业务系统的数据权限的访问控制设计能有所 ...

  3. Java小技巧输出26个英文字母

    相信有的童鞋写到过与字母有关的小东西,是否有写过全部的字母呢?26个这么多字母,一个个打会疯掉.所有咱们可以用一个小技巧使用for循环帮我们把26个字母自动搞出来,大家来瞅一眼把! 使用Java遍历2 ...

  4. 【深度学习】目标检测算法总结(R-CNN、Fast R-CNN、Faster R-CNN、FPN、YOLO、SSD、RetinaNet)

    目标检测是很多计算机视觉任务的基础,不论我们需要实现图像与文字的交互还是需要识别精细类别,它都提供了可靠的信息.本文对目标检测进行了整体回顾,第一部分从RCNN开始介绍基于候选区域的目标检测器,包括F ...

  5. GitHub上整理的一些资料

    技术站点 Hacker News:非常棒的针对编程的链接聚合网站 Programming reddit:同上 MSDN:微软相关的官方技术集中地,主要是文档类 infoq:企业级应用,关注软件开发领域 ...

  6. JVM笔记9-Class类文件结构

    1.Class类文件结构  Class 文件是一组以 8 位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在 Class 文件之中,中间没有添加任何分隔符,这使得整个 Class 文件中 ...

  7. IDEA安装教程

    1.下载安装程序A,链接:https://pan.baidu.com/s/1IAsGDbApfyNsHuS7_m0rdw 密码:fthp 2.下载一个配置程序B,下载安装之后,暂时不用管,之后会用到. ...

  8. i++ 和 ++i;&& 和 &

    一.算数运算符(自增运算符i++.自减运算符i++)  ※  i++是先赋值(计算)再加1 :++i是先加1再赋值(计算) : int m = 5; boolean bool = ++m > 5 ...

  9. Robot framework(RF) Builti,Screenshot和Collections标准库介绍

    1.1  Builti标准类库 在学习一门编程语言的时候,大多教材都是从打印“hello world”开始.我们可以像编程语言一样来学习Robot Framework.虽然通过RIDE 提供“填表”一 ...

  10. ELK 架构之 Elasticsearch 和 Kibana 安装配置

    阅读目录: 1. ELK Stack 简介 2. 环境准备 3. 安装 Elasticsearch 4. 安装 Kibana 5. Kibana 使用 6. Elasticsearch 命令 最近在开 ...