介绍 并发包中的并发List 只有CopyOnWriteArrayList 。CopyOnWriteArrayList 是一个线程安全的ArrayList ,对其进行的修改操作都是在底层的一个复制的数组(快照)上进行的,也就是使用了写时复制策略。
CopyOnWriteArrayList 类图结构如下:
在CopyOnWriteArrayList 的类图中,每个CopyOnWriteArrayList 对象里面有一个array 数组对象用来存放具体元素, ReentrantLock 独占锁对象用来保证同时只有一个线程对array 进行修改。
源码解析 初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public CopyOnWriteArrayList () { setArray(new Object[0 ]); } public CopyOnWriteArrayList (E[] toCopyIn) { setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class)); } public CopyOnWriteArrayList (Collection<? extends E> c) { Object[] elements; if (c.getClass() == CopyOnWriteArrayList.class) elements = ((CopyOnWriteArrayList<?>)c).getArray(); else { elements = c.toArray(); if (elements.getClass() != Object[].class) elements = Arrays.copyOf(elements, elements.length, Object[].class); } setArray(elements); }
添加元素 CopyOnWriteArrayList 中用来添加元素的函数有add(E e)、add(int index, E element)、addIfAbsent(E e)、addAllAbsent(Collection<? extends E> c)等,他们原理类似,以add(E e)为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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(); } }
获取指定位置元素 使用E get(int index)获取下标为index 的元素,如果元素不存在则抛出IndexOutOfBoundsException 异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 public E get (int index) { return get(getArray(), index); } final Object[] getArray() { return array; } private E get (Object[] a, int index) { return (E) a[index]; }
由于执行步骤1 和步骤2 没有加锁,这就可能导致在线程x 执行完步骤1 后执行步骤2 前, 另外一个线程y 进行了remove 操作,导致线程x 返回已被删除的元素,这就是写时复制策略产生的弱一致性问题。
修改元素 使用 E set(int index, E element)修改list 中指定位置元素的值,如果指定位置元素不存在抛出IndexOutOfBoundsException异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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(); } }
删除元素 删除list 里面指定的元素,可以使用E remove(int index)、boolean remove(Object o)和 boolean remove(Object o, Object[] snapshot, int index)等方法,它们的原理一样。以remove(int ind ex)为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 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 ) setArray(Arrays.copyOf(elements, len - 1 )); else { 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(); } }
弱一致性迭代器 所谓弱一致性是指返回迭代器后,其他线程对list 的增删改对迭代器是不可见的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public Iterator<E> iterator () { return new COWIterator<E>(getArray(), 0 ); } static final class COWIterator <E > implements ListIterator <E > { private final Object[] snapshot; private int cursor; private COWIterator (Object[] elements, int initialCursor) { cursor = initialCursor; snapshot = elements; } public boolean hasNext () { return cursor < snapshot.length; } @SuppressWarnings("unchecked") public E next () { if (! hasNext()) throw new NoSuchElementException(); return (E) snapshot[cursor++]; } }
总结 CopyOnWriteArrayList 使用写时复制的策略来保证list 的一致性,而获取 - 修改 - 写入三步操作并不是原子性的,所以在增删改的过程中都使用了独占锁,来保证在某个时间只有一个线程能对list 数组进行修改。另外CopyOnWriteArrayList 提供了弱一致性的迭代器,从而保证在获取迭代器后,其他线程对list 的修改是不可见的,迭代器遍历的数组是一个快照。另外,CopyOnWriteArraySet 的底层就是使用它实现的。