CopyOnWriteArrayList詳解

一、背景java


java誕生之初 就有線程安全的Vector,但Vector對讀寫都是經過synchronized關鍵字來同步的,性能並很差數組

且Vector每次擴容是原來的1倍,存在內存浪費的可能。安全

對於線程安全的List JDK提供了CopyOnWriteArrayList多線程

 

二、原理app


 

2.1 CopyOnWriteArrayList 使用寫時複製技術,讀寫分離ide

對於寫操做,會使用ReentrantLock加鎖,對底層數組拷貝一份,進行修改,修改完後再覆蓋原來的數組性能

 

2.2 底層使用數組this

private transient volatile Object[] array;spa

數據使用volatile修飾,故能保證可見性,存在happens-before關係,使得能讀到最新的數據線程

 

2.3 使用ReentrantLock修改

全部的寫操做 讀會加鎖保證線程安全

但讀操做是沒有任何鎖的,故修改過程當中的數據,此時另外一個線程是讀不到的。

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

  

三、迭代


CopyOnWriteArrayList 迭代時安全失敗的,不容許在迭代過程當中刪除元素,以下會拋出異常

/** * Not supported. Always throws UnsupportedOperationException. * @throws UnsupportedOperationException always; {@code remove} * is not supported by this iterator. */
        public void remove() { throw new UnsupportedOperationException(); } /** * Not supported. Always throws UnsupportedOperationException. * @throws UnsupportedOperationException always; {@code set} * is not supported by this iterator. */
        public void set(E e) { throw new UnsupportedOperationException(); } /** * Not supported. Always throws UnsupportedOperationException. * @throws UnsupportedOperationException always; {@code add} * is not supported by this iterator. */
        public void add(E e) { throw new UnsupportedOperationException(); }
View Code

建立迭代時,引用了原數組,故迭代過程當中如何修改了CopyOnWriteArrayList,

迭代也不會感知。仍是對修改前的數據進行訪問(注意這是針對數據元素的增刪而言,如何存放對象,對象內部的變化仍是有影響的)

static final class COWIterator<E> implements ListIterator<E> {
        /** Snapshot of the array */
        private final Object[] snapshot;
        /** Index of element to be returned by subsequent call to next.  */
        private int cursor;

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

  

 

四、使用場景


缺點:

a. 每次修改都會拷貝數組 佔用內存 寫性能不高

b.修改的過程當中 數據依賴讀的是老的數據 ,只能保證最終一致 不能保證強一致性

 

使用場景

一、多線程 讀多寫少。

二、不要求數據強一致性。

不然推薦使用Collections.synchronziedList

相關文章
相關標籤/搜索