CopyOnWriteArrayList源码详解
来源:互联网 发布:淘宝宝贝像素 编辑:程序博客网 时间:2024/06/04 22:16
1. CopyOnWriteArrayList概述
CopyOnWriteArrayList是线程安全集合,它内部通过一个数组存储元素。通过名字可以看出,这是一个“写时复制”的List,每次要修改该List的时候,都会new一个新的数组,copy原来数组元素到该新的数组,并在该新的数组上修改,修改完后,更新原有数组引用到该新的数组。
读的时候不需要加锁,读操作都是在原来数组上进行的,多线程可以并发的读。但是写是需要加锁的,否则多线程写会创建N个数组。读操作在原有数组进行,写操作在新的数组上进行,互不干扰,读写分离。
“写时复制”会带来很大性能问题,当写操作比较多时,每次修改都将new一个新的数组并将原来数组元素copy到该新的数组,效率较低,因此,CopyOnWriteArrayList适合读远多于写的场景。
看下CopyOnWriteArrayList类图:
CopyOnWriteArrayList实现了List、RandomAccess、Cloneable、Serializable接口。内部通过数组存储元素,因此它是一个“随机存取”的List。RandomAccess是一个空的接口,实现它仅仅标示CopyOnWriteArrayList是一个“随机存取”的List。
3. 构造方法
CopyOnWriteArrayList有三个构造方法,看下源码:
//“写”操作加锁final transient ReentrantLock lock = new ReentrantLock();//内部存储元素的数组private transient volatile Object[] array;//该方法和下面的setArray方法都是包访问权限,用户代码无法访问该方法,因此无法直接修改内部数组//并且这两个方法都是final,无法被子类重写,避免数组被子类修改final Object[] getArray() { return array;}final void setArray(Object[] a) { array = a;}//创建一个空的Listpublic CopyOnWriteArrayList() { setArray(new Object[0]);}//创建的List包括指定集合的元素public CopyOnWriteArrayList(Collection<? extends E> c) { Object[] elements; //如果指定集合也是一个CopyOnWriteArrayList,将引用指向该集合的内部数组即可 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,不能直接引用该数组,因为该数组可以被随意修改,//必须重新创建一个数组并从指定的数组copy元素到新的数组public CopyOnWriteArrayList(E[] toCopyIn) { setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));}
CopyOnWriteArrayList通过可重入锁ReentantLock,对写操作加锁,通过Object数组实现内部存储。
通过指定数组初始化时必须创建一个新的数组,而不能直接将数组引用指向参数数组,因为该参数数组可以被外部随意修改。
2. 增加方法
add方法
CopyOnWriteArrayList有很多增加元素的方法,这些方法都是“写”方法,修改之前需要加锁,首先看最简单的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(); }}
修改元素之前通过Arrays.copyOf方法拷贝一份数组,并在新的数组上修改,修改完后设置数组引用为新建的数组。
CopyOnWriteArrayList还有另外一个在指定位置增加元素的方法,思路基本相同,不再介绍。
接着看下addAll方法
addAll方法
该方法将指定集合的元素都添加到该CopyOnWriteArrayList,看下源码。
public boolean addAll(Collection<? extends E> c) { Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ? ((CopyOnWriteArrayList<?>)c).getArray() : c.toArray(); if (cs.length == 0) return false; final ReentrantLock lock = this.lock; //写操作要加锁! lock.lock(); try { Object[] elements = getArray(); int len = elements.length; //如果本数组大小为0,将数组引用指向cs if (len == 0 && cs.getClass() == Object[].class) setArray(cs); else { //写时复制! //先将elements数组拷贝到一个更大的数组,然后将cs中的元素也拷贝到该更大的数组 Object[] newElements = Arrays.copyOf(elements, len + cs.length); System.arraycopy(cs, 0, newElements, len, cs.length); setArray(newElements); } return true; } finally { //释放锁 lock.unlock(); }}
方法很简单,关键点看注释即可。
还有一个在指定位置插入指定集合元素的方法,思路基本相同,不再讲解。
addAllAbsent
该方法有一个集合参数c,将c中不存在于该CopyOnWriteArrayList的元素加到该CopyOnWriteArrayList中。看下源码:
public int addAllAbsent(Collection<? extends E> c) { Object[] cs = c.toArray(); if (cs.length == 0) return 0; final ReentrantLock lock = this.lock; //写操作必须加锁 lock.lock(); try { Object[] elements = getArray(); int len = elements.length; int added = 0; //这个for循环将c中不存在于该CopyOnWriteArrayList的元素放到cs前面部分 for (int i = 0; i < cs.length; ++i) { Object e = cs[i]; //既不存在于当前CopyOnWriteArrayList的数组,也不存在于cs数组中 if (indexOf(e, elements, 0, len) < 0 && indexOf(e, cs, 0, added) < 0) cs[added++] = e; } //一次性将元素添加到CopyOnWriteArrayList的数组中 if (added > 0) { Object[] newElements = Arrays.copyOf(elements, len + added); System.arraycopy(cs, 0, newElements, len, added); setArray(newElements); } return added; } finally { //释放锁 lock.unlock(); }}
addAllAbsent将最终需要添加到该CopyOnWriteArrayList的元素放到一个数组中,然后一次性添加。
3. 删除方法
remove
//该方法删除指定索引处的元素,返回删除的元素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; //numMoved=0,说明删除的是数组最后一个元素 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(); }}
该删除方法很简单,接着看下另一个重载的方法:
public boolean remove(Object o) { Object[] snapshot = getArray(); //找到元素o的位置 int index = indexOf(o, snapshot, 0, snapshot.length); return (index < 0) ? false : remove(o, snapshot, index);}
该方法删除指定的元素,而不是根据索引删除,首先调用indexOf方法找到该元素在数组中的索引,然后调用另一个重载的删除方法删除该元素。
removeAll
该方法有一个指定集合的参数,删除本List中所有存在于指定集合中的元素。看下源码:
//删除所有存在于集合c中的元素,返回该List是否改变public boolean removeAll(Collection<?> c) { if (c == null) throw new NullPointerException(); final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; if (len != 0) { int newlen = 0; Object[] temp = new Object[len]; for (int i = 0; i < len; ++i) { Object element = elements[i]; //说明当前元素不在指定的集合c中,不需要删除,放在临时数组中 if (!c.contains(element)) temp[newlen++] = element; } //数组有改变,copy一份临时数组并将引用设置为该数组 if (newlen != len) { setArray(Arrays.copyOf(temp, newlen)); return true; } } //未改变 return false; } finally { lock.unlock(); }}
该方法的实现思路也很简单,将存在于指定集合的元素放在一个临时数组,最后copy一份该临时数组,重新设置CopyOnWriteArrayList的数组引用。
CopyOnWriteArrayList还有一个删除指定范围内元素的方法,不再讲解。
4. 迭代器
CopyOnWriteArrayList内部静态类COWIterator实现了迭代器,需要注意的是,这里的迭代器凡是修改操作都会抛出UnsupportedOperationException,即不能通过迭代器修改。看下源码:
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; } 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; } //下面几个修改操作都抛出异常 public void remove() { throw new UnsupportedOperationException(); } public void set(E e) { throw new UnsupportedOperationException(); } 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; }}
这个迭代器只能查看而不能做任务修改。注意forEachRemaining方法,该方法遍历数组元素并处理该元素,关于函数式编程,以后有机会再好好学习下。
- CopyOnWriteArrayList源码详解
- CopyOnWriteArrayList详解
- CopyOnWriteArrayList详解
- CopyOnWriteArrayList详解
- CopyOnWriteArrayList详解
- CopyOnWriteArrayList详解
- CopyOnWriteArrayList详解
- CopyOnWriteArrayList详解
- CopyOnWriteArrayList详解
- CopyOnWriteArrayList详解
- CopyOnWriteArrayList详解
- CopyOnWriteArrayList详解
- CopyOnWriteArrayList详解
- CopyOnWriteArrayList详解
- CopyOnWriteArrayList 详解
- CopyOnWriteArrayList源码解读
- CopyOnWriteArrayList源码阅读
- CopyOnWriteArrayList源码阅读
- 简易Builder模式
- 项目是否还要进行的评估
- Linux系统基本命令行操作(二)
- Tomcat&http
- 第三章 DispatcherServlet详解 ——跟开涛学SpringMVC
- CopyOnWriteArrayList源码详解
- bzoj3171
- 第4周项目4
- 第四周--1
- ElasticSearch学习笔记-相关度得分记录
- Java集合系列(二)
- 常见元组常量和运算
- C#WinForm程序迁移到ubuntu有感
- linuxtest2