Java 併發---CopyOnWriteArrayList

Java併發包裏提供了兩個使用CopyOnWrite機制實現的併發容器,它們是CopyOnWriteArrayList和CopyOnWriteArraySet。多線程

CopyOnWrite原理

CopyOnWrite容器即寫時複製的容器。通俗的理解是當咱們往一個容器添加元素的時候,不直接往當前容器添加,而是先將當前容器進行Copy,複製出一個新的容器,而後新的容器裏添加元素,添加完元素以後,再將原容器的引用指向新的容器。這樣作的好處是咱們能夠對CopyOnWrite容器進行併發的讀,而不須要加鎖,由於當前容器不會添加任何元素。因此CopyOnWrite容器也是一種讀寫分離的思想,讀和寫不一樣的容器。併發

CopyOnWriteArrayList中add方法是須要加鎖的,不然多線程寫的時候會Copy出N個副本出來。網站

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

而get方法不須要加鎖,若是讀的時候有多個線程正在向CopyOnWriteArrayList添加數據,讀仍是會讀到舊的數據,由於寫的時候不會鎖住舊的CopyOnWriteArrayList。this

CopyOnWrite併發容器用於讀多寫少的併發場景。好比白名單,黑名單,商品類目的訪問和更新場景,假如咱們有一個搜索網站,用戶在這個網站的搜索框中,輸入關鍵字搜索內容,可是某些關鍵字不容許被搜索。這些不能被搜索的關鍵字會被放在一個黑名單當中,黑名單天天晚上更新一次。當用戶搜索時,會檢查當前關鍵字在不在黑名單當中,若是在,則提示不能搜索。線程

可是使用CopyOnWriteMap須要注意兩件事情:code

  1. 減小擴容開銷。根據實際須要,初始化CopyOnWriteMap的大小,避免寫時CopyOnWriteMap擴容的開銷。
  2. 使用批量添加。由於每次添加,容器每次都會進行復制,因此減小添加次數,能夠減小容器的複製次數。

CopyOnWrite缺點

CopyOnWrite容器有不少優勢,可是同時也存在兩個問題,即內存佔用問題和數據一致性問題。對象

  1. 內存佔用問題。由於CopyOnWrite的寫時複製機制,因此在進行寫操做的時候,內存裏會同時駐紮兩個對象的內存,舊的對象和新寫入的對象(注意:在複製的時候只是複製容器裏的引用,只是在寫的時候會建立新對象添加到新容器裏,而舊容器的對象還在使用,因此有兩份對象內存)。若是這些對象佔用的內存比較大,好比說200M左右,那麼再寫入100M數據進去,內存就會佔用300M,那麼這個時候頗有可能形成頻繁的Yong GC和Full GC。
  2. 數據一致性問題。CopyOnWrite容器只能保證數據的最終一致性,不能保證數據的實時一致性。因此若是你但願寫入的的數據,立刻能讀到,請不要使用CopyOnWrite容器。
相關文章
相關標籤/搜索