1、什么是CopyOnWritejava
CopyOnWrite(写时复制)简称COW,这是一种利用读写分离的思想来实现线程安全的程序设计思路。顾名思义该思想就是在写的时候将原数据复制一份,而后在新的数据中进行一些写入操做。可是读相关的操做仍是在原有的数据上进行,等到写操做完成以后,用新的数据替换老的数据来达到修改数据的目的。CopyOnWrite只须要在写操做上加锁,在读取的时候也能够进行写操做,因此效率会更好一点。因为在写入操做的时候须要复制一份新的数据,更加占内存,因此适合在写入操做比较少读取操做比较多的状况下来进行操做。数组
2、CopyOnWriteArrayList和CopyOnWriteArraySet安全
java利用了CopyOnWrite这个思想实现了两个线程安全的集合CopyOnWriteArrayList和CopyOnWriteArraySet,其中CopyOnWriteArraySet的底层仍是经过CopyOnWriteArrayList来实现的,因此咱们重点来看看CopyOnWriteArrayList。 app
/** * CopyOnWriteArrayList 是ArrayList的一个线程安全的变体 * 经过在进行add或set等操做的时候复制一份原来的数据 */ public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8673264195747942595L; /** The lock protecting all mutators */ final transient ReentrantLock lock = new ReentrantLock(); /** The array, accessed only via getArray/setArray. */ private transient volatile Object[] array; /** * Gets the array. Non-private so as to also be accessible * from CopyOnWriteArraySet class. */ final Object[] getArray() { return array; } /** * Sets the array. */ final void setArray(Object[] a) { array = a; } /** * Creates an empty list. */ public CopyOnWriteArrayList() { setArray(new Object[0]); } /** * Creates a list holding a copy of the given array. * * @param toCopyIn the array (a copy of this array is used as the * internal array) * @throws NullPointerException if the specified array is null */ public CopyOnWriteArrayList(E[] toCopyIn) { setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class)); } // Positional Access Operations @SuppressWarnings("unchecked") private E get(Object[] a, int index) { return (E) a[index]; } /** * {@inheritDoc} * * @throws IndexOutOfBoundsException {@inheritDoc} */ public E get(int index) { return get(getArray(), index); } /** * 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 { // Not quite a no-op; ensures volatile write semantics setArray(elements); } return oldValue; } finally { lock.unlock(); } } /** * 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) { 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(); } } /** * 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(); } } }
经过上面的一些主要方法能够看到在CopyOnWriteArrayList经过数组来保存数据,其内部建立了一个可重入锁ReentrantLock的实例lock用来对写相关的操做进行加锁,来保证同一时间只有一个副本在进行写操做。CopyOnWriteArrayList不太适合写操做比较多的环境,由于频繁的写操做会产生不少垃圾,是的垃圾回收压力会比较大。dom
3、CopyOnWriteArrayList的迭代器ide
CopyOnWriteArrayList中的迭代器为COWIterator,从代码中能够看出,该迭代器自己是基于当前的数组来建立的,而且迭代器中的方法add、set和remove是不让使用的,这样就避免了和写方法冲突。ui
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; } @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. */ public void remove() { throw new UnsupportedOperationException(); } /** * Not supported. Always throws UnsupportedOperationException. * @throws UnsupportedOperationException always; {@code set} * is not supported by this iterator. */ public void set(E e) { throw new UnsupportedOperationException(); } /** * Not supported. Always throws UnsupportedOperationException. * @throws UnsupportedOperationException always; {@code add} * is not supported by this iterator. */ 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; } }
4、CopyOnWriteArrayList和ArrayList以及Vectorthis
Vector:在这三者中出现的最先,而且是线程安全的,它的读写方法上都使用了synchronized关键字用内置锁对方法进行了加锁,因此读写操做都须要对锁进行竞争,效率通常spa
ArrayList:ArrayList是在vector的基础上去掉了一些同步操做,因此ArrayList是非线程安全的,效率也是三者中最高的。线程
CopyOnWriteArrayList:CopyOnWriteArrayList算是前二者的 一个折中的解决方案,经过COW思想在写操做的时候建立一个数据的副本,而后在副本上进行写操做。读操做在原来的数组上进行,两者互不影响,这样就可以在写的同时也能够进行读操做,在写操做完成后,就用新的数组替换原来的数组,达到数据更新的目的,这也是一种读写分离的思想。因为建立了数据的副本,因此的写入的数据的时候,其余线程读的时候可能没法读取新的数据,这样就不能保证数据的强一致性,只能保证最终一致性。
5、既然使用了锁来对写方法进行了同步为何还要建立一个数据副原本进行操做
我我的的 理解是它须要保证数组中的数据在遍历的时候不能发生改变,因此须要建立一个数据副本。