【併發容器精講2、】CopyOnWriteArrayList

1. 誕生的歷史和緣由

  • 代替Vector和SyschronizedList  就像 ConcurrentHashMap 代替了SyschronizedMap的緣由同樣
  • Vector和SyschronizedList 鎖的粒度太大,併發效率較低,而且迭代時沒法編輯
  • Copy-On-Write  併發容器還包括CopyOnWriteArraySet,用來代替同步 Set

2. 適用場景

  • 讀操做盡量的快一些,而寫即便慢一些也不要緊

3. 讀寫規則

  • 回顧讀寫鎖:讀讀共享、其餘都互斥,(寫寫互斥,讀寫互斥,寫讀互斥)
  • 讀寫鎖規則的升級:讀取是徹底不用加鎖的,而且更厲害的是寫入也不會阻塞讀取操做,只有寫入寫入纔會同步等待

ArrayList數組

 public static void main(String[] args) {        List list=new ArrayList();        list.add("11");        list.add("22");        list.add("33");        Iterator iterator = list.iterator();        while (iterator.hasNext()){            System.out.println(list);            Object next = iterator.next();            System.out.println(next);            if(next.equals("11")){                list.remove("22");            }            if(next.equals("33")){                list.add("2222");            }        }}

圖片CopyOnWriteArrayList併發

public static void main(String[] args) {        CopyOnWriteArrayList list = new CopyOnWriteArrayList();        list.add("11");        list.add("22");        list.add("33");        list.add("44");        Iterator iterator = list.iterator();        while (iterator.hasNext()) {            System.out.println(list);            Object next = iterator.next();            System.out.println(next);            if (next.equals("11")) {                list.remove("22");            }            if (next.equals("33")) {                list.add("2222");            }        }

   }

圖片經過案例咱們能夠發現 ArrayList 迭代修改的時候會 拋出異常 ,而CopyOnWriteArrayList 不會app

4. 實現原理&源碼分析

CopyOnWrite的含義ide

建立新副本、讀寫分離函數

不可變原理源碼分析

迭代的時候this

咱們看下ArrayList 源碼爲何會報錯spa

@SuppressWarnings("unchecked")        public E next() {            checkForComodification();            int i = cursor;            if (i >= size)                throw new NoSuchElementException();            Object[] elementData = ArrayList.this.elementData;            if (i >= elementData.length)                throw new ConcurrentModificationException();            cursor = i + 1;            return (E) elementData[lastRet = i];        }

點開方法,他會進行一個比較操做因此會出現異常3d


       final void checkForComodification() {            if (modCount != expectedModCount)                throw new ConcurrentModificationException();        }    }

咱們看下 CopyOnWriteArrayList 源碼code

這是CopyOnWriteArrayList存放數據的地方,只能經過getArray獲取 而且他會上鎖,用的ReentrantLock


/** The lock protecting all mutators */    final transient ReentrantLock lock = new ReentrantLock(); /** The array, accessed only via getArray/setArray. */    private transient volatile Object[] array;

初始化 :構造函數 新建個空的數組

/**     * Sets the array.     */    final void setArray(Object[] a) {        array = a;    }
   /**     * Creates an empty list.     */    public CopyOnWriteArrayList() {        setArray(new Object[0]);    }

add 方法

 /**     * Appends the specified element to the end of this list.     *     * @param e element to be appended to this list     * @return {@code true} (as specified by {@link Collection#add})     */    public boolean add(E e) {        final ReentrantLock lock = this.lock;        //先拿到鎖        lock.lock();        try {        //獲取初始化數組            Object[] elements = getArray();            //獲取長度 //copy出一個新的數組            Object[] newElements = Arrays.copyOf(elements, len + 1);            //將咱們的參數添加到這個位置            newElements[len] = e;            //set方法添加至            setArray(newElements);            return true;        } finally {        //釋放鎖            lock.unlock();        }    }

get方法

@SuppressWarnings("unchecked")    private E get(Object[] a, int index) {        return (E) a[index];    }
   /**     * {@inheritDoc}     *     * @throws IndexOutOfBoundsException {@inheritDoc}     */    public E get(int index) {        return get(getArray(), index);    }
 public boolean hasNext() {            return cursor < snapshot.length;        }
       public boolean hasPrevious() {            return cursor > 0;        }
       @SuppressWarnings("unchecked")        public E next() {        //判斷 true 和  false        //邏輯簡單了許多            if (! hasNext())                throw new NoSuchElementException();            return (E) snapshot[cursor++];        }

5. 缺點

  • 數據一致性問題 :CopyOnWrite只能保證數據最終一致性的問題,不能保證數據實時一致性,因此但願寫入的數據立刻能讀到,不要使用CopyOnWrite
  • 內存佔用問題:由於CopyOnWrite的寫是複製機制,因此在寫的操做,因此內存中會存在2個對象

我的博客地址:http://blog.yxl520.cn/

相關文章
相關標籤/搜索