CopyOnWrite机制

Posted by 小白 on December 21, 2016

即写时复制。当我们往一个容器添加元素的时候,不直接添加到容器中,而是先将这个容器复制一份,将元素添加到复制的容器中,再将原容器的引用指向复制的新容器。

这样做的好处是可以并发地读且不用加锁,也是一种读写分离的思想。

试着实现一个CopyOnWriteMap:

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Created by Administrator on 2016/12/21.
 */
public class CopyOnWriteMap<K,V> implements Map<K,V>,Cloneable {
    private volatile Map<K,V> internalMap;

    public CopyOnWriteMap(){
        internalMap=new HashMap<K,V>();
    }

    @Override
    public int size() {
        return 0;
    }

    @Override
    public boolean isEmpty() {
        return false;
    }

    @Override
    public boolean containsKey(Object key) {
        return false;
    }

    @Override
    public boolean containsValue(Object value) {
        return false;
    }

    @Override
    public V get(Object key) {
        return internalMap.get(key);
    }

    @Override
    public V put(K key, V value) {
        synchronized (this){
            Map<K,V> newMap=new HashMap<K,V>(internalMap);
            V val=newMap.put(key,value);
            internalMap=newMap;
            return val;
        }
    }

    @Override
    public V remove(Object key) {
        return null;
    }

    @Override
    public void putAll(Map<? extends K, ? extends V> newData) {
        synchronized (this){
            Map<K,V> newMap=new HashMap<>(internalMap);
            newMap.putAll(newData);
            internalMap=newMap;
        }
    }

    @Override
    public void clear() {

    }

    @Override
    public Set<K> keySet() {
        return null;
    }

    @Override
    public Collection<V> values() {
        return null;
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        return null;
    }
}

适用场景

适用读多写少的并发场景。 需注意:

  • 减少扩容开销。根据实际需要,初始化CopyOnWriteMap的大小,避免写时CopyOnWriteMap扩容的开销。
  • 使用批量添加。因为每次添加都会对容器进行复制,所以减少复制次数也是必要的。

缺点:

  • 内存占用。写时会同时驻扎两个对象的内存,容易造成GC。
  • 数据一致性问题。可能导致读到的数据不是最新的,因为写时没有为旧的容器加读锁。