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

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


注:在看这篇文章之前,如果对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. tomcat 配置域名访问应用

    <Host appBase="webapps" autoDeploy="true" name="www.XXX.com" unpack ...

  2. dubbo学习(zz)

    dubbo学习 博客分类: 开源软件   Dubbo是阿里巴巴SOA服务化治理方案的核心框架,每天为2,000+个服务提供3,000,000,000+次访问量支持,并被广泛应用于阿里巴巴集团的各成员站 ...

  3. VMware设置inter共享连接出现空值

    1.打开“网络和共享中心”选择“VMware Virtual Ethernet Adapter for VMnet8”网卡右键属性,选择VMware Bridge Protocol,同时设置ip自动获 ...

  4. springboot深入学习(五)-----spring data、事务

    spring data项目是spring解决数据访问问题的一系列解决方案,包含了大量关系型数据库以及非关系型数据库的访问解决方案. 一.spring data jpa 1.简介 jpa是一套规范,不提 ...

  5. Tomcat服务器的安装和配置

    一.Tomcat下载 可以直接从Apache的网站上下载Tomcat(http://tomcat.apache.org/),进入首页后,在左边Download一栏可选择你要下载的版本,点击便可进入To ...

  6. Python处理微信利器——itchat

    接触itchat是一个偶然,上知乎刷出一个有意思的文章.于是乎运行源码,调错加上查阅博客,发现itchat大有可为. 知乎链接:https://zhuanlan.zhihu.com/p/2578293 ...

  7. keras model.compile(loss='目标函数 ', optimizer='adam', metrics=['accuracy'])

    深度学习笔记 目标函数的总结与整理   目标函数,或称损失函数,是网络中的性能函数,也是编译一个模型必须的两个参数之一.由于损失函数种类众多,下面以keras官网手册的为例. 在官方keras.io里 ...

  8. DDR4控制笔记

      DDR4接口 A[17:0] input 为激活命令提 供行地址,为读.写命令地址输入:提供列地址,也为模式寄存器设 置提供操作码,A[16]只用于8Gb和16Gb,A[17]只用于16Gb,另外 ...

  9. WebLogic 11gR1修改jdk版本

    WebLogic 11gR1默认是支持jdk1.6的 我们可以进入到E:\weblogic\user_projects\domains\base_domain\bin中的修改setDomainEnv. ...

  10. 深入浅出javascript(十二)继承——构造函数继承和组合继承

    #题记: 有一水果类,抽象出属性包括:name(水果品种),price(价格),id(ID号).现有两个子类,分别为苹果,桔子,希望继承水果父类. 一.构造函数继承 构造函数继承相当把父类的属性在子类 ...