本节一起学习CopyOnWriteArrayList类的源码
1.首先看一下类的定义
public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
2.类的变量
/** 重入锁用来保护所有的存取器 */ final transient ReentrantLock lock = new ReentrantLock(); /** 存储数据的数组,多线程可见 */ private transient volatile Object[] array;
3.构造方法
/** * 创建一个空的数组. */ public CopyOnWriteArrayList() { setArray(new Object[0]); } /** * 创建一个包含集合c的数组 */ public CopyOnWriteArrayList(Collection<? extends E> c) { Object[] elements; if (c.getClass() == CopyOnWriteArrayList.class) elements = ((CopyOnWriteArrayList<?>)c).getArray(); else { elements = c.toArray(); // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elements.getClass() != Object[].class) elements = Arrays.copyOf(elements, elements.length, Object[].class); } setArray(elements); } /** * 创建一个包含数组toCopyIn的数组 */ public CopyOnWriteArrayList(E[] toCopyIn) { setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class)); }
/** * Sets the array. */ final void setArray(Object[] a) { array = a; }
4.添加元素
/** * 添加元素e到list的结尾 */ 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);//拷贝长度+1的新数组 newElements[len] = e;//设置结尾下标的值 setArray(newElements);//赋值给array数组 return true; } finally { lock.unlock(); } }
/** * 插入element在下标index位置 */ public void add(int index, E element) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; if (index > len || index < 0)//下标不在有效范围内 throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+len); Object[] newElements; int numMoved = len - index;//需要移动的元素个数 if (numMoved == 0)//如果不需要移动元素个数,也既是插入到结尾 newElements = Arrays.copyOf(elements, len + 1); else {//否则,分段复制,留出index位置 newElements = new Object[len + 1]; System.arraycopy(elements, 0, newElements, 0, index); System.arraycopy(elements, index, newElements, index + 1, numMoved); } newElements[index] = element;//赋值 setArray(newElements);//设置数组 } finally { lock.unlock(); } }
5. 获取元素
// 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); }
6.移除元素
/** * 移除下标位置index的元素 */ 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);//获取下标位置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);//复制旧的数组数据到新的数组,不包含index位置的元素 System.arraycopy(elements, index + 1, newElements, index, numMoved); setArray(newElements); } return oldValue; } finally { lock.unlock(); } }
/** * 移除元素o第一次出现的下标位置的元素 */ public boolean remove(Object o) { Object[] snapshot = getArray(); int index = indexOf(o, snapshot, 0, snapshot.length); return (index < 0) ? false : remove(o, snapshot, index); }
/** * A version of remove(Object) using the strong hint that given * recent snapshot contains o at the given index. */ private boolean remove(Object o, Object[] snapshot, int index) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] current = getArray();//获取最新的数组 int len = current.length; if (snapshot != current) findIndex: {//在查找元素下标后,数组发生了改变,需要再次查找下标 int prefix = Math.min(index, len); for (int i = 0; i < prefix; i++) {//在前半部分重新查找下标位置 if (current[i] != snapshot[i] && eq(o, current[i])) { index = i; break findIndex; } } if (index >= len)//如果下标位置超过len的长度,返回false return false; if (current[index] == o)//仍然在index位置 break findIndex; index = indexOf(o, current, index, len);//在后半部分查找元素 if (index < 0) return false; } Object[] newElements = new Object[len - 1];//将旧的数组元素移动到新的数组中 System.arraycopy(current, 0, newElements, 0, index); System.arraycopy(current, index + 1, newElements, index, len - index - 1); setArray(newElements);//设置数组 return true; } finally { lock.unlock(); } }
7.修改元素
/** * 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(); } }8.遍历操作
public Iterator<E> iterator() { return new COWIterator<E>(getArray(), 0);//使用全局变量数组赋值给副本,进行操作 }
static final class COWIterator<E> implements ListIterator<E> { /** 副本 */ private final Object[] snapshot; /** 指针位置,初始0 */ 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; } }
总结:
1.CopyOnWriteArrayList读操作无锁,线程安全
2.底层使用一个线程可见的数组维护元素,初始容量为0,每增加一个元素,则数组长度+1,通过复制将旧的数组移动到新数组。
3.增删改会使用重入锁进行加锁操作,读取不加锁
4.遍历是针对副本的一个遍历,不会出现并发异常
5.针对读多写少的情况,推荐使用CopyOnWriteArrayList