什么是CopyOnWrite容器?

CopyOnWrite容器即写时复制的容器。简单来讲是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。

这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

比较典型的CopyOnWriteArrayList和CopyOnWriteArraySet就是写时复制容器。

CopyOnWriteArrayList类图

image.png
我们进入CopyOnWriteArrayList#add方法,查看源码,可以看到它的的底层依旧是数组,基于ReentrantLock来做并发控制

add()

  1. public boolean add(E e) {
  2. final ReentrantLock lock = this.lock;
  3. lock.lock();
  4. try {
  5. Object[] elements = getArray();
  6. int len = elements.length;
  7. // 复制一个新的数组
  8. Object[] newElements = Arrays.copyOf(elements, len + 1);
  9. // 把容器中的新元素指向新数组
  10. newElements[len] = e;
  11. // 把新元素添加到新数组
  12. setArray(newElements);
  13. return true;
  14. } finally {
  15. lock.unlock();
  16. }
  17. }

set()

  1. public E set(int index, E element) {
  2. // 获得锁
  3. final ReentrantLock lock = this.lock;
  4. lock.lock();
  5. try {
  6. Object[] elements = getArray();
  7. E oldValue = get(elements, index);
  8. if (oldValue != element) {
  9. int len = elements.length;
  10. Object[] newElements = Arrays.copyOf(elements, len);
  11. newElements[index] = element;
  12. setArray(newElements);
  13. } else {
  14. // Not quite a no-op; ensures volatile write semantics
  15. setArray(elements);
  16. }
  17. return oldValue;
  18. } finally {
  19. lock.unlock();
  20. }
  21. }