java并发(三)CopyOnWriteArrayList源码分析

先看前面一部分

 private static final long serialVersionUID = 8673264195747942595L;//用于序列化

    /** The lock protecting all mutators */
    final transient ReentrantLock lock = new ReentrantLock();//锁

    /** The array, accessed only via getArray/setArray. */
    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;
    }

    /**
     * Creates an empty list.
     */
    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }

    /**
     * Creates a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection of initially held elements
     * @throws NullPointerException if the specified collection is null
     */
    public CopyOnWriteArrayList(Collection<? extends E> c) {
        Object[] elements;
        if (c.getClass() == CopyOnWriteArrayList.class)
            elements = ((CopyOnWriteArrayList<?>)c).getArray();
        else {
            elements = c.toArray();
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elements.getClass() != Object[].class)
                elements = Arrays.copyOf(elements, elements.length, Object[].class);
        }
        setArray(elements);
    }

    /**
     * Creates a list holding a copy of the given array.
     *
     * @param toCopyIn the array (a copy of this array is used as the
     *        internal array)
     * @throws NullPointerException if the specified array is null
     */
    public CopyOnWriteArrayList(E[] toCopyIn) {
        setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
    }

    /**
     * Returns the number of elements in this list.
     *
     * @return the number of elements in this list
     */
    public int size() {
        return getArray().length;
    }

    /**
     * Returns {@code true} if this list contains no elements.
     *
     * @return {@code true} if this list contains no elements
     */
    public boolean isEmpty() {
        return size() == 0;
    }

    /**
     * Tests for equality, coping with nulls.
     */
    private static boolean eq(Object o1, Object o2) {
        return (o1 == null) ? o2 == null : o1.equals(o2);
    }

可以看到初始化,如果没有参数,就会构造一个  大小为0的object数组

其他两个构造方法,一个是toCopyIn的副本,一个传入集合。如果传入的类是CopyOnWriteArrayList,那就直接赋值。否则,会转成数组,如果数组不是Object数组,那就复制成Object数组

以下类似的操作就只介绍一种

添加元素

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

getArray()就是获取当前数组,可以看到,先

Object[] elements = getArray();

这是获取一个快照,这样在多线程的情况下,不用操作原来的数组。因为是添加元素,那这个快照当然要比原来多1,所以复制的时候,len+1 ,最后把这个快照放入当前数组,解开锁

获取指定位置元素

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

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

一般删除,修改,添加,都有快照。所以只要get拿到数组后,就不会存在元素被删除或者修改等情况。

修改

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

也是获取一个快照,获取之前旧的数据。如果当前的数据和旧数据不同,那就更改后,放入数组。

否则直接,返回数组就行。为什么值一样还是要设置?这是为了保证volatile语义。

删除

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

和前面差不多,注意一下,删除后的复制数组要分两次复制,因为中间有个数是不要的。

迭代器

private COWIterator(Object[] elements, int initialCursor) {
            cursor = initialCursor;
            snapshot = elements;
        }

        public boolean hasNext() {
            return cursor < snapshot.length;
        }

        public boolean hasPrevious() {
            return cursor > 0;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            if (! hasNext())
                throw new NoSuchElementException();
            return (E) snapshot[cursor++];
        }

cursor是下标

snapshot是数组快照。

可见它的迭代器是弱一致性的,所谓弱一致性,就是一旦拿到迭代器后,对它进行修改是不会影响到拿到的迭代器的。

发布了82 篇原创文章 · 获赞 49 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/xu505928168/article/details/98653617
今日推荐