當前位置: 妍妍網 > 碼農

CopyOnWrite 思想及其套用場景

2024-02-24碼農

作者:飛狗科技
連結:https://www.jianshu.com/p/d1210a97a086

CopyOnWrite(寫入時復制)思想

CopyOnWrite(簡稱COW,中文意思是:寫入時復制)就是在進行寫操作時,先復制要改變的物件,對副本進行寫操作,完成對副本的操作後,把原有物件的參照指向副本物件。

CopyOnWrite采用了讀寫分離的思想解決了執行緒安全且支持讀多寫少等問題

關鍵點

  • CopyOnWrite適用於讀多寫少的情況,最大程度的提高讀的效率;

  • CopyOnWrite是最終一致性,在寫的過程中,原有的讀的數據是不會發生更新的,只有新的讀才能讀到最新數據;

  • 如何使其他執行緒能夠及時讀到新的數據,需要使用volatile變量;

  • 寫的時候不能並行寫,需要對寫操作進行加鎖;

  • CopyOnWriteArrayList的實作原理

    /** The lock protecting all mutators */
    final transient ReentrantLock lock = new ReentrantLock();
    /** The array, accessed only via getArray/setArray. */
    private transient volatile Object[] array;
    /**
    * 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();
    int len = elements.length;
    Object[] newElements = Arrays.copyOf(elements, len + 1);
    newElements[len] = e;
    setArray(newElements);
    returntrue;
    finally {
    lock.unlock();
    }
    }

    volatile

    volatile (揮發物、易變的):變量修飾詞,只能用來修飾變量。volatile修飾的成員變量在每次被執行緒存取時,都強迫從共享記憶體中重讀該成員變量的值。而且,當成員變量發生變 化時,強迫執行緒將變化值回寫到共享記憶體。這樣在任何時刻,兩個不同的執行緒總是看到某個成員變量的同一個值。

    transient

    transient 只能用來修飾欄位。在物件序列化的過程中,標記為transient的變量不會被序列化。

    CopyOnWrite的缺點

    CopyOnWrite容器有很多優點,但是同時也存在兩個問題,即記憶體占用問題和數據一致性問題。所以在開發的時候需要註意一下。

    記憶體占用問題。

    因為CopyOnWrite的寫時復制機制,所以在進行寫操作的時候,記憶體裏會同時駐紮兩個物件的記憶體,舊的物件和新寫入的物件(註意:在復制的時候只是復制容器裏的參照,只是在寫的時候會建立新物件添加到新容器裏,而舊容器的物件還在使用,所以有兩份物件記憶體)。如果這些物件占用的記憶體比較大,比如說200M左右,那麽再寫入100M數據進去,記憶體就會占用300M,那麽這個時候很有可能造成頻繁的Yong GC和Full GC。之前我們系統中使用了一個服務由於每晚使用CopyOnWrite機制更新大物件,造成了每晚15秒的Full GC,套用響應時間也隨之變長。
    針對記憶體占用問題,可以透過壓縮容器中的元素的方法來減少大物件的記憶體消耗,比如,如果元素全是10進制的數位,可以考慮把它壓縮成36進制或64進制。或者不使用CopyOnWrite容器,而使用其他的並行容器,如ConcurrentHashMap。

    數據一致性問題。

    CopyOnWrite容器只能保證數據的最終一致性,不能保證數據的即時一致性。所以如果你希望寫入的的數據,馬上能讀到,請不要使用CopyOnWrite容器。