Java 中的 CopyOnWrite

1,572 阅读3分钟

CopyOnWrite 介绍

Copy-On-Write简称COW,是一种程序设计中的优化策略,其实现思路是大家都在共享一个内容,当有人想要修改内容的时候,就创建一个改内容的副本,对副本进行修改,然后再将原本的引用指向副本,完成内容的修改。是一种读写分离的并发策略,也是一种延时惰性策略

Java 中的 CopyOnWrite 容器

CopyOnWrite容器,即写时复制容器。我们都知道,在 Java 集合中,ArrayList是非线程安全的,Vector虽然是线程安全的,但是Vector是通过对所有操作都加锁的方式实现线程安全的,性能较差。从 JDK1.5 开始,Java 并发包提供了两个实现了Copy-On-Write的容器,分别是CopyOnWriteArrayListCopyOnWriteArraySet

CopyOnWriteArrayList 实现原理

下面通过分析CopyOnWriteArray的源码,了解CopyOnWriteArrayList是如何实现的

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

final Object[] getArray() {
  return array;
}

final void setArray(Object[] a) {
    array = a;
}

可见,CopyOnWriteArrayListadd(E e)方法加了同步锁,通过Arrays.copyOf()方法,复制出一个长度为原数组的副本,然后将元素添加到新数组里,最后调用settArray方法,将原数组的引用指向新数组

public E get(int index) {
	return get(getArray(), index);
}

读取数据的时候并没有枷锁,如果读的时候正好有其他线程在修改CopyOnWriteArrayList的话,读到的还是旧的数据,因为这时候并不会锁住CopyOnWriteArrayList

JDK 中并没有实现CopyOnWriteMap,如果需要可以参照CopyOnWriteArrayList实现一个

public class CopyOnWriteMap<K, V> implements Map<K, V>, Cloneable {
    private volatile Map<K, V> internalMap;
 
    public CopyOnWriteMap() {
        internalMap = new HashMap<K, V>();
    }
 
    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;
        }
    }
 
    public V get(Object key) {
        return internalMap.get(key);
    }
 
    public void putAll(Map<? extends K, ? extends V> newData) {
        synchronized (this) {
            Map<K, V> newMap = new HashMap<K, V>(internalMap);
            newMap.putAll(newData);
            internalMap = newMap;
        }
    }
}

CopyOnWrite 优缺点

优点:

  • 读取性能很高,因为读取的时候是无锁的,比较适合读多写少的场景
  • 采用读写分离策略,允许读取的时候修改集合数据,没有 fail-fast 机制

缺点:

  • 内存占用问题,因为CopyOnWrite的写时复制机制,当进行写操作,同时又有线程在读取数据的时候,内存里就会同时驻扎两个对象的内存,如果这些对象占用的内存比较大,比如说200M左右,那么再写入100M数据进去,内存就会占用300M,那么这个时候很有可能造成频繁的Yong GC和Full GC
  • 数据一致性问题,CopyOnWrite只保证数据的最终一致性,并不能保证数据的实时一致性。所以对数据实时一致性要求比较高的场景不适合使用CopyOnWrite容器
扫码关注我 一起学习,一起进步