CopyOnWriteArrayList核心源码阅读

CopyOnWriteArrayList是ArrayList的线程安全版本,适用于读多写少的情景。它通过复制现有容器并在新容器中执行写操作来保证线程安全,从而避免在读取时发生冲突。文章详细介绍了其内部使用的ReentrantLock锁机制以及add、set、remove等操作的实现原理。

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

 一、概述


        CopyOnWriteArrayList是ArrayList的一个线程安全变体。它被优化为读取而不是写入的列表。        

        当我们往一个集合容器中写入元素时(添加、修改、删除),并不会直接在集合容器中写入,而是先将当前集合容器进行Copy,复制出一个新的容器,然后新的容器里写入元素,写入操作完成之后,再将原容器的引用指向新的容器。这确保所有读取是一致的且线程安全。

        使用CopyOnWriteArrayList的情况通常是当你对列表的读取次数多于写入次数时。它通常在多个线程需要同时并发读取相同数据的情况下使用,但每次只有一个线程需要写入列表。CopyOnWriteArrayList在写操作方面比ArrayList慢,由于涉及复制,但在读操作方面要快得多,并且在必须保证线程安全的情况下是一个不错的选择。

二、源码阅读


package Thread;

import java.util.*;
import java.util.concurrent.locks.ReentrantLock;

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

    //定义一个ReentrantLock类型的锁(不可被改变、反序列化)
    final transient ReentrantLock lock = new ReentrantLock();

    //定义一个Object类型的数组(私有、反序列化)
    private transient volatile Object[] array;

    /**
     * Gets the array.  Non-private so as to also be accessible
     * from CopyOnWriteArraySet class.
     */

    //定义一个getArray方法,获得原数组(不可改变)
    final Object[] getArray() {
        return array;
    }

    /**
     * Sets the array.
     */

    //定义一个setArray方法,给传入的数组赋值(不可改变),用于覆盖原数组
    final void setArray(Object[] a) {
        array = a;
    }


    //无参构造,将数组初始化为空数组
    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }



    //忽略 unchecked 警告信息
    @SuppressWarnings("unchecked")
    //get方法获取指定下标元素
    private E get(Object[] a, int index) {
        return (E) a[index];
    }

    //get方法重载,内部调用getArray方法
    public E get(int index) {
        return get(getArray(), index);
    }

    //set方法给指定下标设置值,并且返回旧值:复制新数组,在新数组中给指定下标改值,再覆盖原数组
    public E set(int index, E element) {
        //获取ReentrantLock锁对象(不可改变)
        final ReentrantLock lock = this.lock;
        //上锁
        lock.lock();
        try {
            //获取原数组
            Object[] elements = getArray();
            //获取当前数组指定下标位置的元素,使用oldValue指向
            E oldValue = get(elements, index);

            //如果旧元素不等于新元素,复制一个一模一样的数组,替换下标元素,写入array
            if (oldValue != element) {
                //获取原数组长度,用于复制方法的参数
                int len = elements.length;
                //通过Arrays.copyof()方法复制一个新数组
                Object[] newElements = Arrays.copyOf(elements, len);
                //替换下标元素
                newElements[index] = element;
                //写入array,覆盖原数组
                setArray(newElements);
            } else {
                // Not quite a no-op; ensures volatile write semantics
                //旧元素=新元素,即没发生变化时,仍写入array
                setArray(elements);
            }
            //返回旧元素
            return oldValue;
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    //add方法用于在尾部添加新元素:先复制一个新数组,在新数组中添加新元素,最后替换原数组
    public boolean add(E e) {
        //获取ReentrantLock锁对象(不可改变)
        final ReentrantLock lock = this.lock;
        //上锁
        lock.lock();
        try {
            //获取原数组
            Object[] elements = getArray();
            //获取原数组长度,用于复制方法的参数
            int len = elements.length;
            //通过Arrays.copyof()方法复制一个新数组(因为要添加新元素所以新数组长度+1)
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            //将新元素e放入新数组最后一位
            newElements[len] = e;
            //写入array,覆盖原数组
            setArray(newElements);
            //返回true
            return true;
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    //add方法的重载,还需要传入下标,根据下标添加新元素:先复制一个新数组,在新数组中给指定位置添加新元素,最后替换原数组
    public void add(int index, E element) {
        //获取ReentrantLock锁对象(不可改变)
        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;
            //如果为0,说明添加至数组尾部
            if (numMoved == 0)
                //复制一个与原数组一样的新数组,长度为原数组+1
                newElements = Arrays.copyOf(elements, len + 1);
            else {
                //否则创建一个原数组长度+1的新数组
                newElements = new Object[len + 1];
                //复制原数组0到index-1的元素至新数组
                System.arraycopy(elements, 0, newElements, 0, index);
                //复制原数组index+1到尾部的元素至新数组
                System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);
            }
            //将新元素添加至指定位置
            newElements[index] = element;
            //写入array,覆盖原数组
            setArray(newElements);
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    //remove方法,根据下标删除元素:复制新数组,但不复制需要删除的元素,最后覆盖原数组
    public E remove(int index) {
        //获取ReentrantLock锁对象(不可改变)
        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;
            //如果长度为0,则说明要删的时最后一个元素
            if (numMoved == 0)
                //复制一个与原数组一样的新数组,长度为原数组-1,抛弃最后一个元素
                setArray(Arrays.copyOf(elements, len - 1));
            else {
                //如果不是最后一个元素,创建一个新数组,长度为原数组-1
                Object[] newElements = new Object[len - 1];
                //将原数组中0到index-1的元素复制至新数组
                System.arraycopy(elements, 0, newElements, 0, index);
                //将原数组中index+1到末尾的元素复制至新数组
                System.arraycopy(elements, index + 1, newElements, index,
                                 numMoved);
                //写入array,覆盖原数组
                setArray(newElements);
            }
            //返回
            return oldValue;
        } finally {
            //释放锁
            lock.unlock();
        }
    }


    //removeRange方法,删除指定范围内的元素:复制新数组,不复制需要删除的范围内的元素,最后覆盖原数组
    void removeRange(int fromIndex, int toIndex) {
        //获取ReentrantLock锁对象(不可改变)
        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;
            //如果至末尾长度为0,说明toIndex为最后一个元素
            if (numMoved == 0)
                //复制原数组的元素至新数组,长度为newlen
                setArray(Arrays.copyOf(elements, newlen));
            else {
                //toIndex不是最后一个元素,则创建一个新的数组,长度为newlen
                Object[] newElements = new Object[newlen];
                //复制原数组0到formIndex范围内的元素至新数组
                System.arraycopy(elements, 0, newElements, 0, fromIndex);
                //复制原数组toIndex到末尾范围内的元素至新数组
                System.arraycopy(elements, toIndex, newElements,
                                 fromIndex, numMoved);
                //覆盖原数组
                setArray(newElements);
            }
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    //removeAll方法,删除指定集合中的所有元素:复制新数组,不复制需要删除的范围内的元素,最后覆盖原数组
    public boolean removeAll(Collection<?> c) {
        //如果集合为空,抛出空指针异常
        if (c == null) throw new NullPointerException();
        //获取ReentrantLock锁对象(不可改变)
        final ReentrantLock lock = this.lock;
        //上锁
        lock.lock();
        try {
            //获得原数组
            Object[] elements = getArray();
            //获得原数组长度
            int len = elements.length;
            //如果原数组长度不为0
            if (len != 0) {
                // temp array holds those elements we know we want to keep
                //新数组长度
                int newlen = 0;
                //创建一个空数组长度为len
                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;
                }
            }
            //等于,说明没删元素,返回false
            return false;
        } finally {
            //释放锁
            lock.unlock();
        }
    }


   //清空数组
    public void clear() {
        //获取ReentrantLock锁对象(不可改变)
        final ReentrantLock lock = this.lock;
        //上锁
        lock.lock();
        try {
            //创建新数组覆盖原数组
            setArray(new Object[0]);
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    // 添加批量元素
    public boolean addAll(Collection<? extends E> c) {
        // 将需要加入的集合转换为数组
        Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ? ((CopyOnWriteArrayList<?>) c).getArray()
                : c.toArray();
        // 如果长度为0,直接返回false
        if (cs.length == 0)
            return false;
        //获取ReentrantLock锁对象(不可改变)
        final ReentrantLock lock = this.lock;
        //上锁
        lock.lock();
        try {
            //获得原数组
            Object[] elements = getArray();
            //获得原数组长度
            int len = elements.length;
            // 如果原数组为空并且类型为Object,则直接将新数组覆盖给原数组
            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();
        }
    }

    // 对集合内的元素进行排序,需要竞争锁 实现Comparator接口,定义排序规则
    public void sort(Comparator<? super E> c) {
        //获取ReentrantLock锁对象(不可改变)
        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();
        }
    }

    // 比较指定对象与此列表的相等性
    public boolean equals(Object o) {
        // 先比较内存地址 如果相等直接返回true
        if (o == this)
            return true;
        // 判断该对象的类型是否属于List,否则直接返回false
        if (!(o instanceof List))
            return false;

        // 将该对象向上转型为List
        List<?> list = (List<?>) (o);
        // 获取list的迭代器
        Iterator<?> it = list.iterator();
        // 获取原数组
        Object[] elements = getArray();
        int len = elements.length;
        // 遍历原数组 判断元素是否相等,不相等或者该对象元素不够则直接返回false
        for (int i = 0; i < len; ++i)
            if (!it.hasNext() || !eq(elements[i], it.next()))
                return false;
        // 遍历结束后该对象仍然有元素 直接返回false
        if (it.hasNext())
            return false;
        return true;
    }

    // 获取该集合的哈希值
    public int hashCode() {
        //定义hash值为1
        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());
        }
        //返回计算的hash值
        return hashCode;
    }
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值