CopyOnWriteArrayList核心源码阅读

Copy-On-Write简称COW,是一种用于集合的并发访问的优化策略。基本思想是:当我们往一个集合容器中写入元素时(添加、修改、删除),并不会直接在集合容器中写入,而是先将当前集合容器进行Copy,复制出一个新的容器,然后新的容器里写入元素,写入操作完成之后,再将原容器的引用指向新的容器。好处:实现对CopyOnWrite集合容器写入操作时的线程安全,但同时并不影响进行并发的读取操作。所以CopyOnWrite容器也是一种读写分离的思想。

CopyOnWriteArrayList核心源码的阅读:



public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    //ReentrantLock可重入锁
    final transient ReentrantLock lock = new ReentrantLock();
    //内部数组 array 存储数据,用 volatile 修饰,保证可见性,可以直接获取相应位置的对象。
    private transient volatile Object[] array;

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

    /**
     * Sets the array.
     */
    final void setArray(Object[] a) {
        array = a;
    }

  //在无参构造方法中会调用setArray方法,创建了一个长度为0的Object数组
    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }


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

    //根据指定下标,到原数组中读取元素。读取过程中不加锁,允许多个线程并发读取。
    //但是如果读取的时候,有其它线程向集合中添加新元素,此时仍然读取到的是旧数据。
    //因为添加操作没有对原数组加锁。
    public E get(int index) {
    	// 通过getArray()获取array属性值,根据指定下标,从原数组中读取元素
        return get(getArray(), index);
    }

    
    public E set(int index, E element) {
        final ReentrantLock lock = this.lock;
        // 上锁,同一时间内只能有一个线程进入
        lock.lock();
        try {
        	// 获取当前array属性值
            Object[] elements = getArray();
         // 获取当前array指定index下标值
            E oldValue = get(elements, index);
         // 如果新值和旧值不相等
            if (oldValue != element) {	
                int len = elements.length;
             // 复制一份新数组,长度和旧数组一致
                Object[] newElements = Arrays.copyOf(elements, len);
                // 修改新数组index下标值
                newElements[index] = element;
             // 新数组赋值给array属性,替换旧数组
                setArray(newElements);
            } else {
                // Not quite a no-op; ensures volatile write semantics
            	 // 即使新值和旧值一致,为了确保volatile语义,需要重新设置array
            	setArray(elements);
            }
            return oldValue;
        } finally {
        	//释放锁
            lock.unlock();
        }
    }

    //添加新元素至集合时,会将当前数组Copy复制新数组,
    //并将新元素添加至新数组,最后替换原数组。
    //执行过程中,使用ReentrantLock加锁,保证线程安全,避免多个线程复制数组。
    public boolean add(E e) {
    	//加ReentrantLock锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
        	//得到原数组
            Object[] elements = getArray();
            int len = elements.length;
            //复制原数组得到新数组,长度+1是因为要添加一个元素
            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)
            	 // numMoved为0,说明是在末尾添加,过程和add(E e)方法一致
                newElements = Arrays.copyOf(elements, len + 1);
            else {
            	 // 否则创建一个新数组,数组长度为旧数组长度值+1
                newElements = new Object[len + 1];
                // 分两次复制,分别将index之前和index+1之后的元素复制到新数组中
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);
            }
            // 在新数组的index位置添加指定元素
            newElements[index] = element;
           // 新数组赋值给array属性,替换旧数组
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

    //删除指定下标元素。根据指定下标,从原数组中,
    //复制其它元素至新数组,最后替换原数组。
    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)
            	// 如果要删除的是末端数据,则只用复制前面len-1个数据到新副本上,并切换引用
                setArray(Arrays.copyOf(elements, len - 1));
            else {
            	// 若删除的数据在数组中间:
            	// 设置新数组长度为旧数组长度-1,因为是减少一个元素
                Object[] newElements = new Object[len - 1];
                // 分段复制,将index前的元素和index+1后的元素复制到新数组
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index + 1, newElements, index,
                                 numMoved);
                // 设置array
                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);
          //移动的范围是原数组长度-toIndex,将删除范围后的元素统一向前移动
            int numMoved = len - toIndex;
          //如果要移动的范围为0,说明删除仍是在末尾删,无需移动元素
            if (numMoved == 0)
                setArray(Arrays.copyOf(elements, newlen));//将原数组的newlen个长度复制给array数组
            else {
            	//否则就是中间删除,需把下标toIndex后的元素向前移动
                Object[] newElements = new Object[newlen];
                //将原数组从0开始复制到新数组从0开始,复制fromIndex-1个结束
                System.arraycopy(elements, 0, newElements, 0, fromIndex);
                //将原数组从toIndex开始复制到新数组从fromIndex开始,复制numMoved-1个结束
                System.arraycopy(elements, toIndex, newElements,
                                 fromIndex, numMoved);
              //设置array数组为新数组newElements
                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
                //newlen 表示新数组的索引位置,新数组中存在不包含在 c 中的元素
            	int newlen = 0;
            	//定义临时数组用来保存原数组
                Object[] temp = new Object[len];
                // 循环,把不包含在 c 里面的元素,放到新数组中
                for (int i = 0; i < len; ++i) {
                	//拿到原数组中的每个元素
                    Object element = elements[i];
                  //若当前集合c不包含当前元素,说明该元素不用删除
                    if (!c.contains(element))
                    	//将当前元素放在临时数组的长度+1位置
                        temp[newlen++] = element;
                }
             // 拷贝新数组,变相的删除了不包含在 c 中的元素
                if (newlen != len) {
                    setArray(Arrays.copyOf(temp, newlen));
                    return true;
                }
            }
            return false;
        } finally {
            lock.unlock();
        }
    }


   //清空数组
    public void clear() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
        	//更新array数组为空数组
            setArray(new Object[0]);
        } finally {
            lock.unlock();
        }
    }

  //添加c中所有元素进数组
    public boolean addAll(Collection<? extends E> c) {
    	//判断当前对象的Class类型是否等于CopyOnWriteArrayList的Class类型
    	//等于创建当前对象的数组给cs数组
    	//不等于则将c转换成array数组再赋给cs数组
    	Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ?
            ((CopyOnWriteArrayList<?>)c).getArray() : c.toArray();
        //如果长度等于0,返回false
            if (cs.length == 0)
            return false;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
          //若原数组长度为0且cs数组的Class对象等于Object数组的Class对象
            if (len == 0 && cs.getClass() == Object[].class)
            	//设置array数组为cs
            	setArray(cs);
            else {
            	//创建新数组,将原数组赋值给新数组并且长度加上cs数组的长度
                Object[] newElements = Arrays.copyOf(elements, len + cs.length);
                //将cs数组从0开始复制到新数组从len开始,复制cs数组的长度-1个结束
                System.arraycopy(cs, 0, newElements, len, cs.length);
               //更新array数组为新数组
                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);
          //创建es数组指向新数组newElements
            @SuppressWarnings("unchecked") E[] es = (E[])newElements;
            //利用Array工具类的sort方法,按照自定义的比较器实现相应的排序
            Arrays.sort(es, c);
            //更新array数组为新数组
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

    
    public boolean equals(Object o) {
    	//如果对象o等于当前对象返回true
        if (o == this)
            return true;
        //如果对象o的类型不是List类型,返回false
        if (!(o instanceof List))
            return false;
        //将对象o赋给list列表
        List<?> list = (List<?>)(o);
        //创建list的迭代器对象
        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;
      //遍历完数组后,如果当前集合还未遍历完毕,说明当前集合元素比原数组多,返回false
        if (it.hasNext())
            return false;
        return true;
    }

    //计算哈希值
    public int hashCode() {
    	//定义hashCode并赋值为1
        int hashCode = 1;
        //得到原数组
        Object[] elements = getArray();
        //得到原数组长度
        int len = elements.length;
        for (int i = 0; i < len; ++i) {
        	//得到数组中每个元素
            Object obj = elements[i];
            //计算当前元素的哈希值,当前元素为空,则返回0,否则返回哈希值
            hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
        }
        return hashCode;
    }
}
 

CopyOnWriteArrayList具有以下特性:

  1. 在保证并发读取的前提下,确保了写入时的线程安全;
  2. 由于每次写入操作时,进行了Copy复制原数组,所以无需扩容;
  3. 适合读多写少的应用场景。由于add()、set() 、 remove()等修改操作需要复制整个数组,所以会有内存开销大的问题。
  4. CopyOnWriteArrayList由于只在写入时加锁,所以只能保证数据的最终一致性,不能保证数据的实时一致性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值