美文网首页
CopyOnWriteArrayList原理

CopyOnWriteArrayList原理

作者: 光_93e5 | 来源:发表于2018-11-10 19:18 被阅读0次

    源码解析

    CopyOnWriteArrayList 首先它是实现了 List,RandomAccess,Cloneable,Serializable

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

    我们主要看看常用的方法add、get、set、remove方法

    首先是add方法,每步都有注释

       //直接添加一个元素到数组的最后
        public boolean add(E e) {
            //给当前的对象上锁
            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;
                //如果下标大于当前数组长度或者小于0 则抛出下标越界异常
                if (index > len || index < 0)
                    throw new IndexOutOfBoundsException("Index: "+index+
                                                        ", Size: "+len);
                Object[] newElements;
                //得出要插入的下标位置
                int numMoved = len - index;
                //为0表示在数组最后,直接插入
                if (numMoved == 0)
                    newElements = Arrays.copyOf(elements, len + 1);
                else {
                   // 否则表示要插入数组中间,直接创建一份新的数组
                    newElements = new Object[len + 1];
                  //  先拷贝index前部分的内容,在拷贝index后部分的内容
                    System.arraycopy(elements, 0, newElements, 0, index);
                    System.arraycopy(elements, index, newElements, index + 1,
                                     numMoved);
                }
                //在把元素存入当前下标位置
                newElements[index] = element;
               //修改当前对象的初始值
                setArray(newElements);
            } finally {
                 //释放锁
                lock.unlock();
            }
        }
    

    在来看看set方法

        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);
        }
    
       private E get(Object[] a, int index) {
            return (E) a[index];
        }
    
        private E get(Object[] a, int index) {
            return (E) a[index];
        }
    

    最后是remove方法

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

    总结

    可以看到,其中增删改操作都是上了锁的,因此CopyOnWriteArrayList主要是作用在读多写少的场景下,缺点就是如果是需要写上立马就能读到的,那么不建议用这个,因为读是从旧的副本读取的,而写时存储的数据是重新拷贝了一份新的数组了,从而可能导致数据的不一致性。

    相关文章

      网友评论

          本文标题:CopyOnWriteArrayList原理

          本文链接:https://www.haomeiwen.com/subject/knftfqtx.html