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是數組快照。

可見它的迭代器是弱一致性的,所謂弱一致性,就是一旦拿到迭代器後,對它進行修改是不會影響到拿到的迭代器的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章