CopyOnWriteArrayList核心源码阅读

CopyOnWriteArrayList是一种线程安全的集合容器,通过复制原数组来实现并发写入的安全性,允许多线程并发读取而不影响读取性能。它使用ReentrantLock确保写入操作的线程安全,读取时不加锁,提供高效率的并发访问。在添加、修改或删除元素时,会复制原数组并操作新数组,最后更新原数组引用。
摘要由CSDN通过智能技术生成

CopyOnWriteArrayList是一种用于集合的并发访问的优化策略。基本思想是:当我们往一个集合容器中写入元素时(添加、修改、删除),并不会直接在集合容器中写入,而是先将当前集合容器进行Copy,复制出一个新的容器,然后新的容器里写入元素,写入操作完成之后,再将原容器的引用指向新的容器

 好处:实现对CopyOnWrite集合容器写入操作时的线程安全(使用ReentrantLock实现),但同时并不影响进行并发的读取操作。

允许多个线程并发读取,但只能有一个线程写入。

以下是对CopyOnWriteArrayList源码的理解:

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
//    可重入锁,保证线程安全
    final transient ReentrantLock lock = new ReentrantLock();

//    transient让定义的变量不被序列化
    private transient volatile Object[] array;

    /**
     * Gets the array.  Non-private so as to also be accessible
     * from CopyOnWriteArraySet class.
     */
//    获取数组
    final Object[] getArray() {
        return array;
    }

    /**
     * Sets the 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];//传入数组和指定下标,返回数组中指定下标的元素
    }

    
    public E get(int index) {
//    	根据指定下标,调用上面的get()从原数组中读取元素
        return get(getArray(), 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();
        }
    }

//    添加新元素
	/* 主要思想:将当前数组Copy复制新数组,并将新元素添加至新数组,最后替换原数组。 */
    public boolean add(E e) {
//    	使用ReentrantLock加锁,保证线程安全,避免多个线程复制数组
        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;
//            想要添加值的位置如果大于原数组的长度或者小于0,则抛出异常
            if (index > len || index < 0)
                throw new IndexOutOfBoundsException("Index: "+index+
                                                    ", Size: "+len);
            Object[] newElements;//创建新的数组
            int numMoved = len - index;
            if (numMoved == 0)//如果想要添加的指定位置在最后,就直接将原数组复制到新数组上,并将长度加1
                newElements = Arrays.copyOf(elements, len + 1);
            else {//如果想加在最后
                newElements = new Object[len + 1];//新数组的长度是老数组的长度加1
                System.arraycopy(elements, 0, newElements, 0, index);//将0到index之间(不包含index)的元素,从新数组下标为0开始复制到新数组中
                System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);//将老数组从下标为index开始一直到len的元素,从新数组下标为index+1开始复制到新数组中
            }
            newElements[index] = element;//设置新数组index位置的元素为想要加入的那个元素
            setArray(newElements);//把原数组引用指向新数组
        } finally {
//        	关锁
            lock.unlock();
        }
    }

//    删除指定下标元素
	/* 根据指定下标,从原数组中,Copy复制其它元素至新数组,最后替换原数组 */
    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];//因为要删除一个元素,所以创建出的新数组是老数组的长度减1
                System.arraycopy(elements, 0, newElements, 0, index);//复制老数组中0到index之间的元素(不包括index)至新数组中
                System.arraycopy(elements, index + 1, newElements, index,//复制老数组中index+1后面的所有元素
                                 numMoved);
                setArray(newElements);//把原数组引用指向新数组
            }
//            返回要删除的那个元素
            return oldValue;
        } finally {
//        	关闭锁
            lock.unlock();
        }
    }

//删除指定范围
    void removeRange(int fromIndex, int toIndex) {
//    	设置锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
//        	获取原数组
            Object[] elements = getArray();
            int len = elements.length;
//			如果想要删除的范围的起始位置小于0 或者 终止位置大于原数组的长度  或者  终止位置小于起始位置就抛出异常
            if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
                throw new IndexOutOfBoundsException();
//            新数组的长度为原数组长度减去想要删除的范围的长度
            int newlen = len - (toIndex - fromIndex);
//            需要移动的元素个数(即删除指定范围后,指定范围之后没删除的元素需要向前移动的元素个数)
            int numMoved = len - toIndex;
            if (numMoved == 0)//如果需要移动的元素个数为0,则删除的范围在原数组的最后面
                setArray(Arrays.copyOf(elements, newlen));//将原数组中0到newlen的长度复制给新数组
            else {
//            	如果不是,则先创建出长度为newlen的新数组
                Object[] newElements = new Object[newlen];
                //将原数组0到删除范围的起始位置复制给新数组
                System.arraycopy(elements, 0, newElements, 0, fromIndex);
//                将原数组从删除范围的终止位置到len复制给新数组的fromIndex位置到newlen
                System.arraycopy(elements, toIndex, newElements,
                                 fromIndex, numMoved);
                setArray(newElements);//把原数组引用指向新数组
            }
        } finally {
            lock.unlock();//关锁
        }
    }

//传入一个集合,删除原数组中在集合中有的元素(即集合和原数组的交集)
    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;
                }
//                如果新数组和原数组的长度不一样(即有元素被删除)就把原数组引用指向新数组,并返回true
                if (newlen != len) {
                    setArray(Arrays.copyOf(temp, newlen));
                    return true;
                }
            }
//           如果原数组为空则返回false 
            return false;
        } finally {
            lock.unlock();
        }
    }


//   清除数组
    public void clear() {
//    	设置锁
        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();
        if (cs.length == 0)//如果获取的数组为空则返回false
            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();
        }
    }

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

    
//    判断两个数组是否相同
    public boolean equals(Object o) {
//    	如果传入的与要比较的元素的内存地址相同,则这两个值一定相同
        if (o == this)
            return true;
//        判断传入的对象是否是List类型(如果不是则类型都不同,则直接返回false)
        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)
//        	如果两个数组的长度不一样则两个数组一定不一样,返回false
            if (!it.hasNext() || !eq(elements[i], it.next()))
                return false;
        if (it.hasNext())
            return false;
        return true;
    }

//    计算hash值
    public int hashCode() {
        int hashCode = 1;
//        获取原数组
        Object[] elements = getArray();
        int len = elements.length;
//        遍历数组
        for (int i = 0; i < len; ++i) {
            Object obj = elements[i];
//            计算hash值
            hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
        }
//        返回计算的hash值
        return hashCode;
    }
}
 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值