前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java集合-CopyOnWriteArrayList源码解析-JDK1.8

Java集合-CopyOnWriteArrayList源码解析-JDK1.8

作者头像
Java学习录
发布2019-04-18 15:10:43
2630
发布2019-04-18 15:10:43
举报
文章被收录于专栏:Java学习录Java学习录

CopyOnWriteArrayList简介

CopyOnWriteArrayList和ArrayList一样是一个动态数组。而与ArrayList不同的是,它是线程安全的。

建议先阅读 ArrayList源码分析 ,再回来看此文会Soeasy哦!

CopyOnWriteArrayList实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。

  1. List提供了添加、删除、修改、遍历等功能。
  2. RandmoAccess提供了随机访问功能
  3. Cloneable提供了可以被克隆的功能
  4. Serializable提供了序列化的功能

CopyOnWriteArrayList的属性

代码语言:javascript
复制
 /**实现线程安全的锁对象*/    final transient ReentrantLock lock = new ReentrantLock();
    /** 元素缓冲区,volatile保证多线程下始终读取到最新的数据*/    private transient volatile Object[] array;

上方的lock对象就是CopyOnWriteArrayList实现线程安全的秘诀。

对于Java中的锁有疑问的同学可以参考此文章: Java中的锁

CopyOnWriteArrayList的构造方法

代码语言:javascript
复制
         public CopyOnWriteArrayList() {        setArray(new Object[0]);    }

    public CopyOnWriteArrayList(Collection<? extends E> c) {        Object[] elements;        if (c.getClass() == java.util.concurrent.CopyOnWriteArrayList.class)            elements = ((CopyOnWriteArrayList<?>)c).getArray();        else {            elements = c.toArray();            // c.toArray might (incorrectly) not return Object[] (see 6260652)            if (elements.getClass() != Object[].class)                elements = Arrays.copyOf(elements, elements.length, Object[].class);        }        setArray(elements);    }
    public CopyOnWriteArrayList(E[] toCopyIn) {        setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));    }    final void setArray(Object[] a) {        array = a;    }

三个构造方法最后都是调用的setArray方法完成的初始化。

CopyOnWriteArrayList的方法

接下来我们就以CopyOnWriteArrayList的几个比较经典的方法来看一下它是如何设计的。

首先是添加方法:

代码语言:javascript
复制
 /**     * 添加     */    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();        }    }
    /**     * 指定索引添加     */    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();        }    }    final Object[] getArray() {        return array;    }    final void setArray(Object[] a) {        array = a;    }

可以看的,进行添加之前首先是要先获得锁对象才继续进行的操作(包括修改和删除),只有这样才能保证线程安全。

接着看添加的逻辑

新建一个数组,接着将通过getArray()方法获取到的原始的数组拷贝到新数组中,然后将新增数据也添加到新数组中;最后将新数组赋值给原先的数组。

接下来看删除操作:

代码语言:javascript
复制
  /**     * 删除     */    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();        }    }

同添加方法相同的逻辑,先获取锁,然后通过copy数组方式进行删除操作。

接下来修改方法,修改的时候也使用到了查询方法:

代码语言:javascript
复制
/**     * 替换指定索引的元素     */    public E set(int index, E element) {        final ReentrantLock lock = this.lock;        lock.lock();        try {            Object[] elements = getArray();            E oldValue = get(elements, index);
            if (oldValue != element) {                int len = elements.length;                Object[] newElements = Arrays.copyOf(elements, len);                newElements[index] = element;                setArray(newElements);            } else {                // Not quite a no-op; ensures volatile write semantics                setArray(elements);            }            return oldValue;        } finally {            lock.unlock();        }    }     /**     * get方法     */    public E get(int index) {        return get(getArray(), index);    }     @SuppressWarnings("unchecked")    private E get(Object[] a, int index) {        return (E) a[index];    }

第一步同样是加锁,接着会有一个判断,看要修改的索引位置的元素是否相同,不相同则继续通过copy数组的方式进行替换,最后使用setArray()方法更新。

看了CopyOnWriteArrayList的增删改查方法你就应该明白一件事,这哥们除了查询、增删改都很慢呀。

与ArrayList相比,CopyOnWriteArrayList最值得我们注意的地方就是:

  1. 增删改操作必须获取锁之后才能进行,操作完毕释放锁其他操作才可以继续执行
  2. get元素是使用volatile修饰的,可以保证多线程之间的数据更新同步

鉴于篇幅有限,本篇文章仅列出上方部分代码,CopyOnWriteArrayList完整源码解析请点击下方“阅读原文”查看!!!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-04-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java学习录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档