CopyOnWriteArrayList实现原理及源码分析

  CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全且读操做无锁的ArrayList,写操做则经过建立底层数组的新副原本实现,是一种读写分离的并发策略,咱们也能够称这种容器为"写时复制器",Java并发包中相似的容器还有CopyOnWriteSet。本文会对CopyOnWriteArrayList的实现原理及源码进行分析。数组

实现原理

  咱们都知道,集合框架中的ArrayList是非线程安全的,Vector虽是线程安全的,但因为简单粗暴的锁同步机制,性能较差。而CopyOnWriteArrayList则提供了另外一种不一样的并发处理策略(固然是针对特定的并发场景)。安全

  不少时候,咱们的系统应对的都是读多写少的并发场景。CopyOnWriteArrayList容器容许并发读,读操做是无锁的,性能较高。至于写操做,好比向容器中添加一个元素,则首先将当前容器复制一份,而后在新副本上执行写操做,结束以后再将原容器的引用指向新容器。并发

  优缺点分析框架

  了解了CopyOnWriteArrayList的实现原理,分析它的优缺点及使用场景就很容易了。源码分析

  优势:性能

  读操做性能很高,由于无需任何同步措施,比较适用于读多写少的并发场景。Java的list在遍历时,若中途有别的线程对list容器进行修改,则会抛出ConcurrentModificationException异常。而CopyOnWriteArrayList因为其"读写分离"的思想,遍历和修改操做分别做用在不一样的list容器,因此在使用迭代器进行遍历时候,也就不会抛出ConcurrentModificationException异常了this

  缺点:spa

  缺点也很明显,一是内存占用问题,毕竟每次执行写操做都要将原容器拷贝一份,数据量大时,对内存压力较大,可能会引发频繁GC;二是没法保证明时性,Vector对于读写操做均加锁同步,能够保证读和写的强一致性。而CopyOnWriteArrayList因为其实现策略的缘由,写和读分别做用在新老不一样容器上,在写操做执行过程当中,读不会阻塞但读取到的倒是老容器的数据。线程

源码分析

  基本原理了解了,CopyOnWriteArrayList的代码实现看起来就很容易理解了。code

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(); } }  

  添加的逻辑很简单,先将原容器copy一份,而后在新副本上执行写操做,以后再切换引用。固然此过程是要加锁的。

  删除操做

 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) //若是要删除的是列表末端数据,拷贝前len-1个数据到新副本上,再切换引用
                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(); } }

  删除操做同理,将除要删除元素以外的其余元素拷贝到新副本中,而后切换引用,将原容器引用指向新副本。同属写操做,须要加锁。

  咱们再来看看读操做,CopyOnWriteArrayList的读操做是不用加锁的,性能很高。

public E get(int index) { return get(getArray(), index); }

  直接读取便可,无需加锁

 private E get(Object[] a, int index) { return (E) a[index]; }

 总结

  本文对CopyOnWriteArrayList的实现原理和源码进行了分析,并对CopyOnWriteArrayList的优缺点也进行了分析(Java并发包中还提供了CopyOnWriteSet,原理相似)。其实所谓并发容器的优缺点,无非是取决于咱们在面对特定并发场景时,是否能作出相对合理的选择和应用。也但愿本文能帮助到有须要的童鞋,共勉。

相关文章
相关标签/搜索