并发容器一览
  图源:https://time.geekbang.org/column/article/90201?utm_term=pc_interstitial_938
CopyOnWriteArrayList
       该容器支持多个线程同时读(读不加锁),但同一时刻只能一个线程写(写加锁)。
  内部维护了一个数组
1 /** The array, accessed only via getArray/setArray. */
2 private transient volatile Object[] array;
1.获取元素
可以看到没有做任何处理,直接返回数组中的元素。读取元素时是不加锁的,也就是可以任意线程同时读取。
 1 /**
2 * {@inheritDoc}
3 *
4 * @throws IndexOutOfBoundsException {@inheritDoc}
5 */
6 public E get(int index) {
7 return get(getArray(), index);
8 }
9 /**
10 * Gets the array. Non-private so as to also be accessible
11 * from CopyOnWriteArraySet class.
12 */
13 final Object[] getArray() {
14 return array;
15 }
16 @SuppressWarnings("unchecked")
17 private E get(Object[] a, int index) {
18 return (E) a[index];
19 }
2.更新元素
  可以看到,是直接深拷贝了原数组,然后修改新数组的值,最后将对象的数组指向新数组。更新元素是加了可重入锁的,同一时刻只有一个线程能够修改数组的值,其他线程要修改只能等待获得锁。
 1 /**
2 * Replaces the element at the specified position in this list with the
3 * specified element.
4 *
5 * @throws IndexOutOfBoundsException {@inheritDoc}
6 */
7 public E set(int index, E element) {
8 final ReentrantLock lock = this.lock;
9 lock.lock();
10 try {
11 Object[] elements = getArray();
12 E oldValue = get(elements, index);
13 // 判断原有位置元素是否和插入元素相同,不相同才插入
14 if (oldValue != element) {
15 int len = elements.length;
16 Object[] newElements = Arrays.copyOf(elements, len);
17 newElements[index] = element;
18 setArray(newElements);
19 } else {
20 // Not quite a no-op; ensures volatile write semantics
21 // 不是无用操作,是为了保证volatile写的意义
22 setArray(elements);
23 }
24 return oldValue;
25 } finally {
26 lock.unlock();
27 }
28 }
29 /**
30 * Sets the array.
31 */
32 final void setArray(Object[] a) {
33 array = a;
34 }
22行代码处的setArray(elements),这里操作看似没有意义。因为elements是volatile修饰的, 这里是为了保证happens-before原则:
  1.volatile变量读操作happens-before写操作;
  2.同一线程的代码顺序执行,先前的操作 happens-before 之后的操作
  3.传递性
实际上这是为了保证非volatile变量的可见性。
像下面这个例子,线程1的set操作可以保证nonVolatileField 变量的修改对线程2的读取可见。
// initial conditions
int nonVolatileField = 0;
CopyOnWriteArrayList<String> list = /* a single String */ // Thread 1
nonVolatileField = 1; // (1)
list.set(0, "x"); // (2) // Thread 2
String s = list.get(0); // (3)
if (s == "x") {
int localVar = nonVolatileField; // (4)
}
  好像JDK11移除了这行代码。。。

3.插入元素
 1 /**
2 * Appends the specified element to the end of this list.
3 * 末尾增加元素
4 * @param e element to be appended to this list
5 * @return {@code true} (as specified by {@link Collection#add})
6 */
7 public boolean add(E e) {
8 final ReentrantLock lock = this.lock;
9 lock.lock();
10 try {
11 Object[] elements = getArray();
12 int len = elements.length;
13 Object[] newElements = Arrays.copyOf(elements, len + 1);
14 newElements[len] = e;
15 setArray(newElements);
16 return true;
17 } finally {
18 lock.unlock();
19 }
20 }
21 /**
22 * Inserts the specified element at the specified position in this
23 * list. Shifts the element currently at that position (if any) and
24 * any subsequent elements to the right (adds one to their indices).
25 *
26 * @throws IndexOutOfBoundsException {@inheritDoc}
27 */
28 public void add(int index, E element) {
29 final ReentrantLock lock = this.lock;
30 lock.lock();
31 try {
32 Object[] elements = getArray();
33 int len = elements.length;
34 if (index > len || index < 0)
35 throw new IndexOutOfBoundsException("Index: "+index+
36 ", Size: "+len);
37 Object[] newElements;
38 int numMoved = len - index;
39 if (numMoved == 0)
40 newElements = Arrays.copyOf(elements, len + 1);
41 else {
42 newElements = new Object[len + 1];
43 System.arraycopy(elements, 0, newElements, 0, index);
44 System.arraycopy(elements, index, newElements, index + 1,
45 numMoved);
46 }
47 newElements[index] = element;
48 setArray(newElements);
49 } finally {
50 lock.unlock();
51 }
52 }

  可以看到,增加元素和更新元素一样,也是创建一个新数组复制原有元素,然后插入新增元素。同时也是加了可重入锁,同一时刻只有一个线程能进行修改。

4.删除元素
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices). Returns the element that was removed from the list.
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
E oldValue = get(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 oldValue;
} finally {
lock.unlock();
}
}
  可以看到也是创建了一个新数组,直接复制原数组元素。然后将数组引用指向新数组。

并发容器-CopyOnWriteArrayList的更多相关文章

  1. Java并发编程原理与实战三十四:并发容器CopyOnWriteArrayList原理与使用

    1.ArrayList的实现原理是怎样的呢? ------>例如:ArrayList本质是实现了一个可变长度的数组. 假如这个数组的长度为10,调用add方法的时候,下标会移动到下一位,当移动到 ...

  2. 多线程并发容器CopyOnWriteArrayList

    原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容 ...

  3. Java并发容器——CopyOnWriteArrayList

    CopyOnWriteArrayList是“读写分离”的容器,在写的时候是先将底层源数组复制到新数组中,然后在新数组中写,写完后更新源数组.而读只是在源数组上读.也就是,读和写是分离的.由于,写的时候 ...

  4. Java并发机制(5)--同步容器与并发容器

    Java并发编程:同步容器整理自:博客园-海子-http://www.cnblogs.com/dolphin0520/p/3933404.html1.同步容器出现原因 常用的ArrayList,Lin ...

  5. JAVA 多线程随笔 (三) 多线程用到的并发容器 (ConcurrentHashMap,CopyOnWriteArrayList, CopyOnWriteArraySet)

    1.引言 在多线程的环境中,如果想要使用容器类,就需要注意所使用的容器类是否是线程安全的.在最早开始,人们一般都在使用同步容器(Vector,HashTable),其基本的原理,就是针对容器的每一个操 ...

  6. CopyOnWriteArrayList并发容器

    CopyOnWriteArrayList并发容器 Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才 ...

  7. 09 jdk1.5的并发容器:CopyOnWriteArrayList(转载)

    原文链接:http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW,是一种用于程序设计中的优化策略. 其基本思路是,从一开始大家都在共享同一个内容 ...

  8. 【java并发容器】并发容器之CopyOnWriteArrayList

    原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容 ...

  9. Java并发指南14:Java并发容器ConcurrentSkipListMap与CopyOnWriteArrayList

    原文出处http://cmsblogs.com/ 『chenssy』 到目前为止,我们在Java世界里看到了两种实现key-value的数据结构:Hash.TreeMap,这两种数据结构各自都有着优缺 ...

随机推荐

  1. IDA 创建本地类型

    在IDA中我们常常使用 shift+F9打开结构体视图,ins 创建结构体,但操作有些繁琐. 我们可以在View-->Open Subviews-->Local Types(视图--> ...

  2. 【数据库】Redis(2)--Redis的常用数据类型及命令

    1.Redis主要数据类型分类 Redis中存储数据常用的数据类型主要有五种:String.List.Set.Sorted Set.Hash,这五种数据结构在Redis中存储数据的命令掌握对于我们后期 ...

  3. Scrum完整项目实例

    一.背景 在谈 JIRA 之前,就不得不说说敏捷开发了.正式由于项目是基于敏捷开发进行的,因此才引入了 JIRA 这款适合于敏捷开发的项目管理工具.当然,这里不会大篇章的介绍敏捷开发,之前的文章有详细 ...

  4. 树结构系列(三):B树、B+树

    树结构系列(三):B树.B+树 文章首发于「陈树义」公众号及个人博客 shuyi.tech,欢迎访问更多有趣有价值的文章. 文章首发于「陈树义」公众号及个人博客 shuyi.tech 平衡二叉树的查找 ...

  5. 一文搞懂MySQL体系架构!!

    写在前面 很多小伙伴工作很长时间了,对于MySQL的掌握程度却仅仅停留在表面的CRUD,对于MySQL深层次的原理和技术知识了解的少之又少,随着工作年限的不断增长,职场竞争力却是不断降低的.很多时候, ...

  6. oo第二单元——多线程魔鬼电梯

    在初步认识了面向对象思想后,立刻进入了多线程的学习,本单元的难点主要是锁的理解,需要保证线程安全的同时防止死锁的发生,也要尽可能缩小锁的范围,提高性能.这一单元以电梯为载体,让我们从生活出发,从电梯运 ...

  7. 两种纯CSS方式实现hover图片pop-out弹出效果

    实现原理 主要图形的组成元素由背景和前景图两个元素,以下示例代码中,背景元素使用伪元素 figure::before 表示, 前景元素使用 figure img 表示,当鼠标 hover 悬浮至 fi ...

  8. 【剑指offer】10:矩形覆盖

    题目描述: 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形.请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? 解题思路: ①方法一 对于这种题没有思路怎么办?可以先从最 ...

  9. 一文简述JAVA内部类和异常

    内部类和异常 内部类 在一个类的内部定义的一个类,例如,A类中定义了一个B类,则B类相对于A类就是内部类,而A类相对于B类就是外部类 成员内部类 静态内部类 局部内部类 匿名内部类 成员内部类 pub ...

  10. 11. VUE 数组操作

    变异方法 Vue 包含一组观察数组的变异方法,所以它们也将会触发视图更新.这些方法如下: push() 添加元素 <ul id="example-1"> <li ...