CopyOnWriteArrayList核心源码阅读

CopyOnWriteArrayList是一个线程安全的列表实现,通过使用ReentrantLock和在修改时复制原数组来确保并发读写的正确性。在写操作时加锁并复制新数组,读操作无需加锁,可能导致读到旧值。文章详细解析了其add、set、remove等操作的内部机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述:CopyOnWriteArrayList是实现了List接口的线程安全类:它在我们进行写操作(增、删、改)首先都加了ReentrantLock锁,其次在进行写操作的时候会将原数组拷贝一份生成新数组,在新数组上进行修改,最后让原数组指向新数组。在源码中能够看见它对读操作并未加锁,这可能在多个线程同时读会读到旧的值,为了更加的了解它的内部是如何,下面我们对核心源码部分进行查看。

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {

   //ReentrantLock锁(唯一)
    final transient ReentrantLock lock = new ReentrantLock();

    //CopyOnWriteArrayList用于存储数据的数组(Object[])
    private transient volatile Object[] array; 

 final Object[] getArray() {
        return array;
    }

    //作用:返回CopyOnWriteArrayList用于存储数据的数组(Object[] array)

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

   //作用:为数组设置值

  public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }

 //作用:初始化当前数组为一个空数组[]

 @SuppressWarnings("unchecked")
    private E get(Object[] a, int index) {
        return (E) a[index];
    }

   /**
     * @param a
     * @param index
     * 作用:返回Object[]中指定下标(index)的元素
     * 直接按下标在数组中取值并返回
     */

   public E get(int index) {
        return get(getArray(), index);
    }

/**

  *       作用:返回数组中指定下标位置的元素

  *       通过调用它上面重载的方法get(Object[] a, int index)完成

  */

    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();
        }
    }

    /**
     *  作用:设置指定下标位置元素的值
     *  步骤:1、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
     *       2、获取当前类中的Object[],并获取指定下标元素的原始值
     *       3、判断原始值和设置的值是否相等
     *          相等,则不做改变再次把数组set回去
     *          不等,1)获取数组长度,并将整个Object[]赋值一份产生一个新数组
     *                    2)将新数组的指定下标改为指定值
     *                    3)让原始数组指向新数组
     */

    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、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
     *       2、获取当前Object[]和数组长度len
     *       3、复制当前数组产生长度为len+1的新数组
     *       4、让新数组的末尾元素【下标为len】值为添加的元素
     *       5、让原始数组指向新数组完成添加操作并返回true
     */

    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();
        }
    }

    /**
     *  作用:向集合中的指定位置添加元素
     *  步骤:1、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
     *       2、获取当前Object[]和数组长度len
     *       3、判断传入的下标是否合法(0<=index<len),不合法抛出IndexOutOfBoundsException
     *       4、定义新数组,计算需要移动的元素个数
     *       5、若需移动的元素个数为0(即末尾添加指定元素),将原数组复制len+1个长度赋值给新数组
     *       6、若需移动数!=0【即不在末尾】
     *          6.1)新数组初始化长度为len+1
     *          6.2)复制指定位置的前半部分【从0开始】到新数组【0-index】
     *          6.3)复制后半部分(从index开始)到新数组【index+1,需移动元素数】空出index位置
     *       7、为新数组的index位置附上要添加的元素值
     *       8、让原数组指向新数组完成添加操作
     */

    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();
        }
    }


    /**
     * 作用:移除集合中指定下标位置的元素
     * 步骤:1、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
     *      2、获取当前Object[]和数组长度len
     *      3、获取指定下标元素的原始值并存储
     *      4、获取需移动的元素数并判断
     *          4.1)若为0,即删除末尾元素,复制原数组的len-1个长度并赋值给原数组
     *          4.2)不为0,复制前半部分(从0开始)到新数组(0-index)
     *              复制后半部分(从index+1开始)【跳过index位置】到新数组(index,移动数)
     *      5、设置原数组的值为新数组完成删除操作
     */

    void removeRange(int fromIndex, int toIndex) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;

            if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
                throw new IndexOutOfBoundsException();
            int newlen = len - (toIndex - fromIndex);
            int numMoved = len - toIndex;
            if (numMoved == 0)
                setArray(Arrays.copyOf(elements, newlen));
            else {
                Object[] newElements = new Object[newlen];
                System.arraycopy(elements, 0, newElements, 0, fromIndex);
                System.arraycopy(elements, toIndex, newElements,
                                 fromIndex, numMoved);
                setArray(newElements);
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 作用:移除集合中指定下标范围内的元素
     * 步骤:1、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
     *      2、获取当前Object[]和数组长度len
     *      3、判断传入的下标是否合法(0,len),不合法抛出IndexOutOfBoundsException
     *      4、计算数组的新长度newLen(移除后)、需移动的元素个数
     *      5、若移动数为0(即从fromIndex到末尾全部移除),则复制原数组(从0开始)newLen个长度到新数组
     *        若移动数不为0,复制前半部分(从0开始)到新数组(0)【fromIndex个长度】
     *        复制后半部分(从toIndex开始)到新数组(从fromIndex)【长度为需移动的个数】
     *      6、设置原数组的值为新数组
     */

    public boolean removeAll(Collection<?> c) {
        if (c == null) throw new NullPointerException();
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (len != 0) {
                // temp array holds those elements we know we want to keep
                int newlen = 0;
                Object[] temp = new Object[len];
                for (int i = 0; i < len; ++i) {
                    Object element = elements[i];
                    if (!c.contains(element))
                        temp[newlen++] = element;
                }
                if (newlen != len) {
                    setArray(Arrays.copyOf(temp, newlen));
                    return true;
                }
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 作用:移除该集合中 含有的指定集合的元素(补集)
     * 步骤:1、传入的集合为null,抛出NullPointerException
     *      2、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
     *      3、获取当前Object[]和数组长度len
     *      4、判断长度:
     *          4.1)若不为0,定义一个新长度newLen为0(累加,保存最后所剩的元素个数)
     *               4.1.1)创建一个原数组长度len的Object[]
     *               4.1.2)遍历原Object[],取出每一个元素判断是否包含于集合c中:
         *              不包含则将该元素存入创建的新数组中,同时newLen++
     *              4.1.3)如果新数组长度和原数组长度不等,则需要复制新数组的newLen个元素赋值给原数组,同时返回true
     *          4.2)若为0,返回false
     */

    public void clear() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            setArray(new Object[0]);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 作用:清空集合中的内容为[]
     * 步骤:1、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
     *      2、创建一个长度为0的Object[]赋值给原数组
     */

    public boolean addAll(Collection<? extends E> c) {
        Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ?
            ((CopyOnWriteArrayList<?>)c).getArray() : c.toArray();
        if (cs.length == 0)
            return false;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (len == 0 && cs.getClass() == Object[].class)
                setArray(cs);
            else {
                Object[] newElements = Arrays.copyOf(elements, len + cs.length);
                System.arraycopy(cs, 0, newElements, len, cs.length);
                setArray(newElements);
            }
            return true;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 作用:向原数组中添加指定集合中的元素
     * 参数:cs数组
     * 步骤:1、创建一个Object[]cs,判断传入集合类型和当前集合的类型是否相同:
     *          1.1)相同,则直接初始化为传入集合的数组
     *          1.2)不同,则将传入集合转换为数组完成cs的初始化
     *      2、判断cs的长度,长度为0返回false
     *      3、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
     *      4、获取当前Object[]和数组长度len
     *      5、判断是否 原数组长度为0 并且 cs的Class对象和Object[]的class对象一致
     *          5.1)是,将cs数组直接赋值给原数组
     *          5.2)不是,创建创建新数组(长度为原数组长度+集合的长度),同时完成整个原数组的复制
     *              接着将集合中的元素(从0开始)复制到新数组中(从len开始)【长度为集合的长度】
     *              设置原数组的值为新数组
     *      6、返回true
     */

    public void sort(Comparator<? super E> c) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            Object[] newElements = Arrays.copyOf(elements, elements.length);
            @SuppressWarnings("unchecked") E[] es = (E[])newElements;
            Arrays.sort(es, c);
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 作用:对数组按照一定比较规则进行排序
     * 参数:传入Comparator接口【它的实现类重写compare()定义了比较规则】
     * 步骤:1、获取当前ReentrantLock锁,并对代码块加锁(保证操作的原子性)
     *      2、创建Object[]获取当前数组对象
     *      3、创建新Objectp[]newElements初始化为当前数组的备份
     *      4、在创建一个新Object[]cs指向newElements
     *      5、通过Arrays工具类的静态方法sort传入cs和比较器Comparator对cs数组进行原地排序
     *      6、设置当前数组为newElements
     */

    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof List))
            return false;

        List<?> list = (List<?>)(o);
        Iterator<?> it = list.iterator();
        Object[] elements = getArray();
        int len = elements.length;
        for (int i = 0; i < len; ++i)
            if (!it.hasNext() || !eq(elements[i], it.next()))
                return false;
        if (it.hasNext())
            return false;
        return true;
    }

    /**
     * 作用:用于比较对象
     * 步骤:1、比较当前对象和传入对象的内存地址,相同返回true
     *      2、判断当前对象是否为一个List类型,不是返回false
     *      3、将传入的Object对象转换为List类型,并获取List集合的迭代器iterator()
     *      4、获取当前Object[]和数组长度len
     *      5、遍历Object[],同时List集合通过迭代器遍历
     *          比较每一个元素,如果迭代器中没有下一个元素 || Object[]和List的第i个元素内容不相等返回false
     *          获取原数组遍历结束,但List中还有元素返回false
     *      否则返回true
     */

    public int hashCode() {
        int hashCode = 1;
        Object[] elements = getArray();
        int len = elements.length;
        for (int i = 0; i < len; ++i) {
            Object obj = elements[i];
            hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
        }
        return hashCode;
    }

    /**
     * 作用:获取对象的哈希值
     * 步骤:1、定义hashCode变量并初始化为1
     *      2、获取当前Object[]和数组长度
     *      3、遍历当前的Object[],取出每一个元素
     *          元素不为null,hashCode每次为原hashCode的31倍并加上当前元素的哈希值
     *      4、循环结束,返回哈希值
     */

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值