此文已由作者赵计刚授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。


注:在看这篇文章之前,如果对ArrayList底层不清楚的话,建议先去看看ArrayList源码解析。

http://www.cnblogs.com/java-zhao/p/5102342.html

1、对于CopyOnWriteArrayList需要掌握以下几点

  • 创建:CopyOnWriteArrayList()

  • 添加元素:即add(E)方法

  • 获取单个对象:即get(int)方法

  • 删除对象:即remove(E)方法

  • 遍历所有对象:即iterator(),在实际中更常用的是增强型的for循环去做遍历

注:CopyOnWriteArrayList是一个线程安全,读操作时无锁的ArrayList。

2、创建

public CopyOnWriteArrayList()

使用方法:

List<String> list = new CopyOnWriteArrayList<String>();

相关源代码:

    private volatile transient Object[] array;//底层数据结构

    /**
     * 获取array
     */
    final Object[] getArray() {
        return array;
    }     /**
     * 设置Object[]
     */
    final void setArray(Object[] a) {
        array = a;
    }     /**
     * 创建一个CopyOnWriteArrayList
     * 注意:创建了一个0个元素的数组
     */
    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }

注意点:

  • 设置一个容量为0的Object[];ArrayList会创造一个容量为10的Object[]

3、添加元素

public boolean add(E e)

使用方法:

list.add("hello");

源代码:

    /**
     * 在数组末尾添加元素
     * 1)获取锁
     * 2)上锁
     * 3)获取旧数组及其长度
     * 4)创建新数组,容量为旧数组长度+1,将旧数组拷贝到新数组
     * 5)将要增加的元素加入到新数组的末尾,设置全局array为新数组
     */
    public boolean add(E e) {
        final ReentrantLock lock = this.lock;//这里为什么不直接用this.lock(即类中已经初始化好的锁)去上锁
        lock.lock();//上锁
        try {
            Object[] elements = getArray();//获取当前的数组
            int len = elements.length;//获取当前数组元素
            /*
             * Arrays.copyOf(elements, len + 1)的大致执行流程:
             * 1)创建新数组,容量为len+1,
             * 2)将旧数组elements拷贝到新数组,
             * 3)返回新数组
             */
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;//新数组的末尾元素设成e
            setArray(newElements);//设置全局array为新数组
            return true;
        } finally {
            lock.unlock();//解锁
        }
    }

注意点:

  • Arrays.copyOf(T[] original, int newLength)该方法在ArrayList中讲解过

疑问:

  • 在add(E)方法中,为什么要重新定义一个ReentrantLock,而不直接使用那个定义的类变量锁(全局锁)

    • 答:事实上,按照他那样写,即使是在add、remove、set中存在多个引用,最后也是一个实例this.lock,所以不管你在add、remove、set中怎样去从新定义一个ReentrantLock,其实add、remove、set中最后使用的都是同一个锁this.lock,也就是说,同一时刻,add/remove/set只能有一个在运行。这样讲,就是说,下边这段代码完全可以做一个修改。修改前的代码:

          public boolean add(E e) {
              final ReentrantLock lock = this.lock;//这里为什么不直接用this.lock(即类中已经初始化好的锁)去上锁
              lock.lock();//上锁

      修改后的代码:

          public boolean add(E e) {
              //final ReentrantLock lock = this.lock;//这里为什么不直接用this.lock(即类中已经初始化好的锁)去上锁
              this.lock.lock();//上锁
  • 根据以上代码可知,每增加一个新元素,都要进行一次数组的复制消耗,那为什么每次不将数组的元素设大(比如说像ArrayList那样,设置为原来的1.5倍+1),这样就会大大减少因为数组元素复制所带来的消耗?

4、获取元素

public E get(int index)

使用方法:

list.get(0)

源代码:

    /**
     * 根据下标获取元素
     * 1)获取数组array
     * 2)根据索引获取元素
     */
    public E get(int index) {
        return (E) (getArray()[index]);
    }

注意点:

  • 获取不需要加锁

疑问:在《分布式Java应用:基础与实践》一书中作者指出:读操作会发生脏读,为什么?

从类属性部分,我们可以看到array数组是volatile修饰的,也就是当你对volatile进行写操作后,会将写过后的array数组强制刷新到主内存,在读操作中,当你读出数组(即getArray())时,会强制从主内存将array读到工作内存,所以应该不会发生脏读才对呀!!!

补:volatile的介绍见《附2 volatile》,链接如下:

http://www.cnblogs.com/java-zhao/p/5125698.html

免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请点击。

相关文章:
【推荐】 10本最热门科普书免费送!人工智能数学物理获奖经典佳作!

CopyOnWriteArrayList源码解析(1)的更多相关文章

  1. ArrayList、CopyOnWriteArrayList源码解析(JDK1.8)

    本篇文章主要是学习后的知识记录,存在不足,或许不够深入,还请谅解. 目录 ArrayList源码解析 ArrayList中的变量 ArrayList构造函数 ArrayList中的add方法 Arra ...

  2. CopyOnWriteArrayList源码解析

    Java并发包提供了很多线程安全的集合,有了他们的存在,使得我们在多线程开发下,可以和单线程一样去编写代码,大大简化了多线程开发的难度,但是如果不知道其中的原理,可能会引发意想不到的问题,所以知道其中 ...

  3. 第三章 CopyOnWriteArrayList源码解析

    注:在看这篇文章之前,如果对ArrayList底层不清楚的话,建议先去看看ArrayList源码解析. http://www.cnblogs.com/java-zhao/p/5102342.html ...

  4. CopyOnWriteArrayList源码解析(2)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 5.删除元素 public boolean remove(Object o) 使用方法: list.remo ...

  5. CopyOnWriteArraySet源码解析

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 注:在看这篇文章之前,如果对CopyOnWriteArrayList底层不清楚的话,建议先去看看CopyOn ...

  6. 第四章 CopyOnWriteArraySet源码解析

    注:在看这篇文章之前,如果对CopyOnWriteArrayList底层不清楚的话,建议先去看看CopyOnWriteArrayList源码解析. http://www.cnblogs.com/jav ...

  7. EventBus源码解析 源码阅读记录

    EventBus源码阅读记录 repo地址: greenrobot/EventBus EventBus的构造 双重加锁的单例. static volatile EventBus defaultInst ...

  8. EventBus3.0源码解析

    本文主要介绍EventBus3.0的源码 EventBus是一个Android事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递. EventBus使用简单,并将事件发布和订阅充 ...

  9. Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例

    概要 上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具体实现类进行讲解:首先,讲解List,而List中ArrayList又最为常用.因此,本章我们讲解Arra ...

随机推荐

  1. 【UI测试】--帮助设施

  2. 用php把access数据库导入到mysql

    <?php header("content-Type: text/html; charset=utf-8"); /// ///把access数据库转换成mysql的SQL语句 ...

  3. 【搜索】棋盘问题(DFS)

    Description 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别.要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子 ...

  4. python入门之文件处理

    1.读取文件 f=open(file="C:\BiZhi\新建文本文档.txt",mode="r",encoding="utf-8") da ...

  5. 2018.10.29 NOIP训练 数据结构(带修改莫队)

    传送门 带修莫队板题. 直接按照经典写法做就行了. 代码

  6. 证明2x2正交矩阵专置后还是正交矩阵

    [ x1  x2 y1   y2] x1^2+y1^2=1 x2^2 + y2^2=1 x1*x2  + y1*y2=0 如果专置后还是 x1^2 + x2^2=1 y1^2  +y2^2=1 x1* ...

  7. java中的四种修饰符:private、protected、public和default的区别

      本类 本包 子类 包外 public 可以 可以 可以 可以 protected 可以 可以 可以 不可以 default 可以 可以 不可以 不可以 private 可以 不可以 不可以 不可以 ...

  8. POJ 3110 Jenny's First Exam (贪心)

    题意:告诉你n 个科目的考试日期,在考试当天不能复习,每一个科目的最早复习时间不能早于考试时间的t天,每一天你可以复习完一科,也只能复习一科,求最晚的复习时间!. 析:由于题目给定的时间都在1900 ...

  9. celery 4.1下报kombu.exceptions.EncodeError: Object of type 'bytes' is not JSON serializable 处理方式

    #python代码如下 from celery import Celeryimport subprocess app = Celery('tasks', broker='redis://localho ...

  10. matlab生成滤波器系数组

    用MATLAB生成的滤波器系数是可以控制增益的,一般归一化的目的是控制增益为1.滤波器的阶数由数据速率,过渡带宽.通带波纹和阴带波纹来决定, 在下图中FS,Apass,Astop固定之后,只要Fpas ...