Copy-On-Write简称COW,是一种用于集合的并发访问的优化策略。基本思想是:当我们往一个集合容器中写入元素时(添加、修改、删除),并不会直接在集合容器中写入,而是先将当前集合容器进行Copy,复制出一个新的容器,然后新的容器里写入元素,写入操作完成之后,再将原容器的引用指向新的容器。
这样做的好处:实现对CopyOnWrite集合容器写入操作时的线程安全,但同时并不影响进行并发的读取操作。所以CopyOnWrite容器也是一种读写分离的思想。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发集合容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。
CopyOnWriteArrayList相当于线程安全的ArrayList,内部存储结构采用Object[]数组,线程安全使用ReentrantLock实现,允许多个线程并发读取,但只能有一个线程写入。
CopyOnWriteArrayList常用方法:添加新元素
- /**
- * 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) {
- //使用ReentrantLock 加锁,保证线程安全
- 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();
- }
- }
根据指定下标,到原数组中读取元素。允许多个线程并发读取。
- /**
- * {@inheritDoc}
- *
- * @throws IndexOutOfBoundsException {@inheritDoc}
- */
- public E get(int index) {
- //根据指定下标,从原数组中读取元素
- return get(getArray(), index);
- }
-
- @SuppressWarnings("unchecked")
- private E get(Object[] a, int index) {
- return (E) a[index];
- }
-
删除指定下标元素,根据指定下标,从原数组中,Copy复制其他数组元素至新数组,最后调换数组。
- /**
- * Removes the element at the specified position in this list.
- * Shifts any subsequent elements to the left (subtracts one from their
- * indices). Returns the element that was removed from the list.
- *
- * @throws IndexOutOfBoundsException {@inheritDoc}
- */
- public E remove(int index) {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- Object[] elements = getArray();
-
- //获取数组长度
- int len = elements.length;
- //获取指定下标
- E oldValue = get(elements, index);
- int numMoved = len - index - 1;
- //判断删除的是否最后一个元素
- if (numMoved == 0)
- //如果是,复制原数组,长度-1
- setArray(Arrays.copyOf(elements, len - 1));
- else {
- //否则,创建一个新数组,长度-1
- Object[] newElements = new Object[len - 1];
- //复制一个除删除元素之外的新数组
- System.arraycopy(elements, 0, newElements, 0, index);
- System.arraycopy(elements, index + 1, newElements, index,
- numMoved);
- setArray(newElements);
- }
- return oldValue;
- } finally {
- lock.unlock();
- }
- }
set(index,element),按照指定下标,修改/添加元素。
- /**
- * Replaces the element at the specified position in this list with the
- * specified element.
- *
- * @throws IndexOutOfBoundsException {@inheritDoc}
- */
- public E set(int index, E element) {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- //获取源数组
- Object[] elements = getArray();
- E oldValue = get(elements, index);
- //判断新值和旧值是否相同
- if (oldValue != element) {
- int len = elements.length;
- //相同的话,不更新值,然后更新源数组(数组内容没变)
- Object[] newElements = Arrays.copyOf(elements, len);
- newElements[index] = element;
- setArray(newElements);
- } else {
- // 不同的话复制源数组,更新值,然后更新源数组
- setArray(elements);
- }
- return oldValue;
- } finally {
- lock.unlock();
- }
- }
(5)clear方法
清空所有元素
- /**
- * Removes all of the elements from this list.
- * The list will be empty after this call returns.
- */
- public void clear() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- //创建一个新数组,并让其重新指向
- setArray(new Object[0]);
- } finally {
- lock.unlock();
- }
- }
3.CopyOnWriteArrayList特性:add()、set() 、 remove()等修改操作需要复制整个数组,所以会有内存开销大的问题。CopyOnWriteArrayList由于只在写入时加锁,所以只能保证数据的最终一致性,不能保证数据的实时一致性