当前位置: 代码迷 >> 综合 >> CopyOnWriteArrayList 实现原理
  详细解决方案

CopyOnWriteArrayList 实现原理

热度:45   发布时间:2023-11-20 01:17:03.0

    CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全的ArrayList,写操作通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可以称这种容器为"写时复制器",Java并发包中类似的容器还有CopyOnWriteSet,不过在CopyOnWriteSet中任然是调用的是CopyOnWriteArrayList。

实现原理

    集合框架中的ArrayList是非线程安全的,Vector虽是线程安全的,但是它的锁同步机制十分简单所以性能较差。而CopyOnWriteArrayList则提供了另一种不同的并发处理策略(读写分离)。

 CopyOnWriteArrayList容器允许并发读,读操作是无锁的,性能较高。至于写操作,比如向容器中添加一个元素,则首先将当前容器复制一份,然后在新副本上执行写操作,结束之后再将原容器的引用指向新容器。

    /** The lock protecting all mutators */final transient ReentrantLock lock = new ReentrantLock();/** The array, accessed only via getArray/setArray. */private transient volatile Object[] array;/*** 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;//拷贝一份原数组并+1,生成的新数组的len位置上增加元素Object[] newElements = Arrays.copyOf(elements, len + 1);newElements[len] = e;//替换原数组setArray(newElements);return true;} finally {//释放锁lock.unlock();}}

    可以看到它在新增元素的时候会获取锁然后生成一个新的数组+1,这时候再将新数组的新增位置添加新元素,最后替换原数组就行了。

   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;//表示是末尾,直接拷贝原数组0-(len-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();}}

    删除操作同理,把要删除的元素之外的其他元素拷贝到新数组中,然后切换引用,将原数组引用指向新数组。同属写操作,需要加锁。

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

     获取元素无需加锁。

优缺点

优点:

  1. CopyOnWriteArrayList由于其"读写分离"的思想,遍历和修改操作分别作用在不同的数组,所以在使用迭代器进行遍历时候,不会抛出ConcurrentModificationException异常。
  2. 读操作性能很高,因为无需任何同步措施,比较适用于读多写少的并发场景。

缺点:

  1. 内存占用问题,每次执行写操作都要将原容器拷贝一份,数据量大时,对内存压力较大,可能会引起频繁GC。
  2. 无法保证实时性,Vector对于读写操作均加锁同步,可以保证读和写的强一致性。而CopyOnWriteArrayList的实现策略是写和读分别作用在新老不同数组上,在写操作执行过程中,读不会阻塞但读取到的却是为更新后的数据。