CopyOnWriteArrayList和CopyOnWriteArraySet从数据结构类型上来说是类似的,都是用数组实现的保存一组数据的数据结构,区别也简单就是List和set的区别。因此这里就先讨论CopyOnWriteArrayList,然后再说CopyOnWriteArraySet。

这里重点关注的是CopyOnWrite,从字面上很容易理解,每当写操作的时候复制存储数据的数组,把拷贝上修改完再覆盖原先的数组。那么这样的数据结构适用的情况必然是读操作占绝大多数(很少进行写操作),且数据量不大的场合(例如黑名单)。

因此再来看CopyOnWriteArrayList的实现就很好理解了,必然会有一个volatile的数组,一把锁用于写操作(读操作的时候是不要锁的)。

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

复制整个数组使用Arrays.copyOf,部分复制数组使用System.arraycopy。下面以add方法为例:

    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];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
newElements[index] = element;
setArray(newElements);
} finally {
lock.unlock();
}
}

可以看到如果在最后添加元素,则使用Arrays.copyOf新建并复制整个数组,如果在中间插入,则使用System.arraycopy分两段复制,然后插入元素。setArray方法的作用就是覆盖原来的数组,代码如下:

    final void setArray(Object[] a) {
array = a;
}

下面来简单谈谈CopyOnWriteArraySet,其实CopyOnWriteArraySet就是一个CopyOnWriteArrayList的包装类:

private final CopyOnWriteArrayList<E> al;

区别在于因为Set不允许重复元素,因此CopyOnWriteArraySet的add方法调用的是CopyOnWriteArrayList的addIfAbsent方法

    public boolean add(E e) {
return al.addIfAbsent(e);
}

《java.util.concurrent 包源码阅读》08 CopyOnWriteArrayList和CopyOnWriteArraySet的更多相关文章

  1. 《java.util.concurrent 包源码阅读》 结束语

    <java.util.concurrent 包源码阅读>系列文章已经全部写完了.开始的几篇文章是根据自己的读书笔记整理出来的(当时只阅读了部分的源代码),后面的大部分都是一边读源代码,一边 ...

  2. 《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分

    这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int ...

  3. 《java.util.concurrent 包源码阅读》02 关于java.util.concurrent.atomic包

    Aomic数据类型有四种类型:AomicBoolean, AomicInteger, AomicLong, 和AomicReferrence(针对Object的)以及它们的数组类型, 还有一个特殊的A ...

  4. 《java.util.concurrent 包源码阅读》04 ConcurrentMap

    Java集合框架中的Map类型的数据结构是非线程安全,在多线程环境中使用时需要手动进行线程同步.因此在java.util.concurrent包中提供了一个线程安全版本的Map类型数据结构:Concu ...

  5. 《java.util.concurrent 包源码阅读》17 信号量 Semaphore

    学过操作系统的朋友都知道信号量,在java.util.concurrent包中也有一个关于信号量的实现:Semaphore. 从代码实现的角度来说,信号量与锁很类似,可以看成是一个有限的共享锁,即只能 ...

  6. 《java.util.concurrent 包源码阅读》06 ArrayBlockingQueue

    对于BlockingQueue的具体实现,主要关注的有两点:线程安全的实现和阻塞操作的实现.所以分析ArrayBlockingQueue也是基于这两点. 对于线程安全来说,所有的添加元素的方法和拿走元 ...

  7. 《java.util.concurrent 包源码阅读》09 线程池系列之介绍篇

    concurrent包中Executor接口的主要类的关系图如下: Executor接口非常单一,就是执行一个Runnable的命令. public interface Executor { void ...

  8. 《java.util.concurrent 包源码阅读》05 BlockingQueue

    想必大家都很熟悉生产者-消费者队列,生产者负责添加元素到队列,如果队列已满则会进入阻塞状态直到有消费者拿走元素.相反,消费者负责从队列中拿走元素,如果队列为空则会进入阻塞状态直到有生产者添加元素到队列 ...

  9. 《java.util.concurrent 包源码阅读》10 线程池系列之AbstractExecutorService

    AbstractExecutorService对ExecutorService的执行任务类型的方法提供了一个默认实现.这些方法包括submit,invokeAny和InvokeAll. 注意的是来自E ...

随机推荐

  1. PHP知识大全

    --------------------------------------------------------- PHP知识大全 ---------------------------------- ...

  2. [译]ASP.NET Core 2.0 带初始参数的中间件

    问题 如何在ASP.NET Core 2.0向中间件传入初始参数? 答案 在一个空项目中,创建一个POCO(Plain Old CLR Object)来保存中间件所需的参数: public class ...

  3. Numpy的小总结

    1.Numpy是什么? numpy是Python的一个科学计算库,提供矩阵运算的功能. 1.1Numpy的导入 import numpy as np #一般都是用numpy的别名来进行操作 1.2Nu ...

  4. APUE 4 - 线程<2> : 线程同步

    当控件的多个线程共享统一内存时,我们需要确定各个线程访问到的数据的一致性.在cpu结构中,修改操作由多个内存读写周期(memory cycle),而在这些内存周期之间, 有可能会发生其他线程的内存读操 ...

  5. 网络地址转换NAT

    1. 网络地址转换:用于专用网内部的主机和因特网上的主机通信.在专用网连接到因特网 的路由器上需要安装NAT软件,装有NAT软件的路由器叫做NAT路由器,它至少要有 一个有效的全球IP地址.所有使用本 ...

  6. .NET Core 使用RSA算法 加密/解密/签名/验证签名

    前言 前不久移植了支付宝官方的SDK,以适用ASP.NET Core使用支付宝支付,但是最近有好几位用户反应在Linux下使用会出错,调试发现是RSA加密的错误,下面具体讲一讲. RSA在.NET C ...

  7. 1_ROS学习

    1_搭建树莓派环境 树莓派是一个嵌入式计算机,专门用来做嵌入式开发的.我们组上用的树莓派是raspberry pi 3 mode B,5V供电,最大电流不超过2A: 我们需要在树莓派上安装上操作系统. ...

  8. app.get is not a function解决方案

    在express4.x中app.js被申明为一个模块,而不是一个主程序入口,在文件的最后暴露出了这个模块,如下所示 app.js module.exports = app; 但是我们在routes目录 ...

  9. [Bayesian] “我是bayesian我怕谁”系列 - Continuous Latent Variables

    打开prml and mlapp发现这部分目录编排有点小不同,但神奇的是章节序号竟然都为“十二”. prml:pca --> ppca --> fa mlapp:fa --> pca ...

  10. Spring读书笔记——bean创建(下)

    有关Spring加载bean系列,今天这是最后一篇了,主要接上篇对于从Spring容器中获取Bean的一些细节实现的补充. <Spring读书笔记--bean加载>--Spring如何加载 ...