前言

ArrayList 是一个不安全的容器,在多线程调用 add 方法的时候会出现 ArrayIndexOutOfBoundsException 异常,而 Vector 虽然安全,但由于其 add 方法和 get 方法都使用了 synchronized 关键字,导致在并发时的性能令人担忧,因此,伟大的 Doug Lea 编写了 CopyOnWriteArrayList 并发容器,用于替代并发时的 ArrayList,而该类的类名叫 “写的时候拷贝集合”。也非常符合他的设计,那么,我们就看看他是如何实现的。

源码剖析

既然是 ArrayList ,第一个看的当然是 add 方法。

add 方法源码

      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);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}

该方法步骤如下:

  1. 使用重入锁锁住代码块。
  2. 调用 getArray 方法获取当前数组,调用 Arrays 工具类的 copyof 方法,在原来数组长度的基础上加一创建一个新数组,然后将元素添加到新数组的最后以为,这点和 ArrayList 不同, ArrayList 需要扩容的时候在原有的基础上扩容一半。
  3. 调用 setArray 方法,将新数组赋值到成员变量 array 中,注意:该变量是 volatile 的。因此,其他线程可以立即看到他。最后释放锁。

再看看 get 方法:

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

可以看到该方法非常简单:获取到成员变量 array,根据下标获取。没有使用锁,完全支持并发。

从 add 方法和 get 方法中,我们看出了作者的意图,Doug Lea 认为容器 get 的操作比 add 的操作频繁,使用了类似读写分离的方式,读读操作完全并发,而写的时候,并不修改原有的内容,这对于保证当前在读线程的数据一致性非常重要,然后对原有的数据进行一次复制,将改写的内容写入到副本中,写完之后,再将修改完的副本替换原来的数据。这样就可以保证写操作不会影响读了。同时使用 volatile 变量,也保证了内存可见性,更新之后立即就能被其他线程看到。

该类保证了并发时的安全,同时,相比于 Vector 性能要高出很多(读读完全并发)。而 Vector 同时只能有一个线程进行读写,简直可怕。

但该类也不是完美的。该类的迭代器不支持 remove 操作,也不支持 set 操作,也不支持 add 操作。在迭代器中调用这 三个方法将抛出 UnsupportedOperationException 异常。

但是该类可以在 for 循环中做删除操作,这点和 ArrayList 也是不一样的,因为该类每次删除之后也都是 拷贝重写赋值。而 ArrayList 使用 for 循环删除实际上使用的迭代器的 next 方法,而迭代器每次都会检查ArrayList 的状态,调用 checkForComodification 方法,因此会抛出 ConcurrentModificationException 异常。ArrayList 必须使用迭代器的 remove 方法。

        public void remove() {
throw new UnsupportedOperationException();
} public void set(E e) {
throw new UnsupportedOperationException();
} public void add(E e) {
throw new UnsupportedOperationException();
}

总结

总的来说,并发环境下,强烈建议使用该类代替 ArrayList,该类的读读操作可保证完全并发。支持 for 循环做删除操作。不支持迭代器remove ,set, add。

并发编程之 CopyOnWriteArrayList 源码剖析的更多相关文章

  1. 并发编程之 ConcurrentLinkedQueue 源码剖析

    前言 今天我们继续分析 java 并发包的源码,今天的主角是谁呢?ConcurrentLinkedQueue,上次我们分析了并发下 ArrayList 的替代 CopyOnWriteArrayList ...

  2. 并发编程之 ThreadLocal 源码剖析

    前言 首先看看 JDK 文档的描述: 该类提供了线程局部 (thread-local) 变量.这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局 ...

  3. 并发编程之 LinkedBolckingQueue 源码剖析

    前言 JDK 1.5 之后,Doug Lea 大神为我们写了很多的工具,整个 concurrent 包基本都是他写的.也为我们程序员写好了很多工具,包括我们之前说的线程池,重入锁,线程协作工具,Con ...

  4. 并发编程之 AQS 源码剖析

    前言 JDK 1.5 的 java.util.concurrent.locks 包中都是锁,其中有一个抽象类 AbstractQueuedSynchronizer (抽象队列同步器),也就是 AQS, ...

  5. 并发编程之 Exchanger 源码分析

    前言 JUC 包中除了 CountDownLatch, CyclicBarrier, Semaphore, 还有一个重要的工具,只不过相对而言使用的不多,什么呢? Exchange -- 交换器.用于 ...

  6. 并发编程之 Condition 源码分析

    前言 Condition 是 Lock 的伴侣,至于如何使用,我们之前也写了一些文章来说,例如 使用 ReentrantLock 和 Condition 实现一个阻塞队列,并发编程之 Java 三把锁 ...

  7. 并发编程之 Semaphore 源码分析

    前言 并发 JUC 包提供了很多工具类,比如之前说的 CountDownLatch,CyclicBarrier ,今天说说这个 Semaphore--信号量,关于他的使用请查看往期文章并发编程之 线程 ...

  8. 并发编程之 CyclicBarrier 源码分析

    前言 在之前的介绍 CountDownLatch 的文章中,CountDown 可以实现多个线程协调,在所有指定线程完成后,主线程才执行任务. 但是,CountDownLatch 有个缺陷,这点 JD ...

  9. 多线程进阶——JUC并发编程之CountDownLatch源码一探究竟

    1.学习切入点 JDK的并发包中提供了几个非常有用的并发工具类. CountDownLatch. CyclicBarrier和 Semaphore工具类提供了一种并发流程控制的手段.本文将介绍Coun ...

随机推荐

  1. map 小模板~~~ 写的不好 继续添加

    #include<map>#include<string.h>#include<iostream>using namespace std; int main(){  ...

  2. poj2828 线段树单点更新

    Buy Tickets Time Limit: 4000 MS Memory Limit: 65536 KB 64-bit integer IO format: %I64d , %I64u Java ...

  3. FastReport报表设计(仔细看)

    FastReport报表设计 2011-06-16 16:56:19|  分类: 系统开发|举报|字号 订阅     下载LOFTER我的照片书  |     目录 5.1 前言 5.2 基本概念及操 ...

  4. 5. ASP.NET MVC 中的Areas【区域】是什么

    [PS返回上一篇:-->4.ASP.NET MVC 5.0 视图之模型绑定] 从ASP.NET MVC 2.0开始,微软就提供了一个新特性:Areas[区域].Areas仅仅是用来将大型程序拆分 ...

  5. 关于IE9 table显示错位的问题

    首先,win10无法安装IE9,所以需要用IE11模拟IE9,这样:http://www.w10zj.com/Win10xy/Win10yh_638.html: 其次,table显示错位的可能原因:h ...

  6. MySQL随手记

    一.MySQL数据迁移(由远端主机迁移到本地) 1.导出数据库mysqldump -u root -p db > dump_db_date.sqlroot: 账户db: 需要导出的数据库名 2. ...

  7. 【xsy1012】KSHKM的基因工程 AC自动机DP

    题目大意:给你$n$个串$p_i$,最后再给一个串$s$(字符集均为A,C,G,T四个字符中的一个).问你串$s$最少要更改多少个字符(更改后的字符也只能是ACGT),才能满足s中不包含$p_i$$( ...

  8. 善用Intellij插件可大幅提升我们的效率

    转自 :https://www.jianshu.com/p/686ba0ae4ac2 1. .ignore 生成各种ignore文件,一键创建git ignore文件的模板,免得自己去写 截图: 2. ...

  9. (转)python中的selectors模块

    原文:https://www.cnblogs.com/yinheyi/p/8127871.html https://www.rddoc.com/doc/Python/3.6.0/zh/library/ ...

  10. Git for Windows之基础环境搭建与基础操作

    一.安装Git工具 下载地址:Git For Windows 下载完后,安装,全程Next. 二.全局配置 1.配置本地用户名,用于提交代码 2.配置邮箱 三.创建本地Git项目仓库 1.建立代码仓库 ...