写入时复制(CopyOnWrite)
什么是CopyOnWrite容器
CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为写方法不会影响到读的容器。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。读不加锁,写要加。(写包含了add,remove,用ReentrantLock加锁)
CopyOnWriteArrayList的实现原理
在使用CopyOnWriteArrayList之前,我们先阅读其源码了解下它是如何实现的。以下代码是向CopyOnWriteArrayList中add方法的实现(向CopyOnWriteArrayList里添加元素),可以发现在添加的时候是需要加锁的,否则多线程写的时候会Copy出N个副本出来。
类的内部类:COWIterator类
static final class COWIterator<E> implements ListIterator<E> { /** Snapshot of the array */ // 快照 private final Object[] snapshot; /** Index of element to be returned by subsequent call to next. */ // 游标 private int cursor; // 构造函数 private COWIterator(Object[] elements, int initialCursor) { cursor = initialCursor; snapshot = elements; } // 是否还有下一项 public boolean hasNext() { return cursor < snapshot.length; } // 是否有上一项 public boolean hasPrevious() { return cursor > 0; } // next项 @SuppressWarnings("unchecked") public E next() { if (! hasNext()) // 不存在下一项,抛出异常 throw new NoSuchElementException(); // 返回下一项 return (E) snapshot[cursor++]; } @SuppressWarnings("unchecked") public E previous() { if (! hasPrevious()) throw new NoSuchElementException(); return (E) snapshot[--cursor]; } // 下一项索引 public int nextIndex() { return cursor; } // 上一项索引 public int previousIndex() { return cursor-1; } /** * Not supported. Always throws UnsupportedOperationException. * @throws UnsupportedOperationException always; {@code remove} * is not supported by this iterator. */ // 不支持remove操作 public void remove() { throw new UnsupportedOperationException(); } /** * Not supported. Always throws UnsupportedOperationException. * @throws UnsupportedOperationException always; {@code set} * is not supported by this iterator. */ // 不支持set操作 public void set(E e) { throw new UnsupportedOperationException(); } /** * Not supported. Always throws UnsupportedOperationException. * @throws UnsupportedOperationException always; {@code add} * is not supported by this iterator. */ // 不支持add操作 public void add(E e) { throw new UnsupportedOperationException(); } @Override public void forEachRemaining(Consumer<? super E> action) { Objects.requireNonNull(action); Object[] elements = snapshot; final int size = elements.length; for (int i = cursor; i < size; i++) { @SuppressWarnings("unchecked") E e = (E) elements[i]; action.accept(e); } cursor = size; } }