天天看点

关于CopyOnWriteArrayList的技术理解

前言

项目源代码中使用了CopyOnWriteArrayList类,出于好奇就研究它,看它里面有那些优点,作者设计基于什么考虑,能带来那些好处。

原注释如下

/**
 * A thread-safe variant of {@link java.util.ArrayList} in which all mutative
 * operations ({@code add}, {@code set}, and so on) are implemented by
 * making a fresh copy of the underlying array.
 *
 * <p>This is ordinarily too costly, but may be <em>more</em> efficient
 * than alternatives when traversal operations vastly outnumber
 * mutations, and is useful when you cannot or don't want to
 * synchronize traversals, yet need to preclude interference among
 * concurrent threads.  The "snapshot" style iterator method uses a
 * reference to the state of the array at the point that the iterator
 * was created. This array never changes during the lifetime of the
 * iterator, so interference is impossible and the iterator is
 * guaranteed not to throw {@code ConcurrentModificationException}.
 * The iterator will not reflect additions, removals, or changes to
 * the list since the iterator was created.  Element-changing
 * operations on iterators themselves ({@code remove}, {@code set}, and
 * {@code add}) are not supported. These methods throw
 * {@code UnsupportedOperationException}.
 *
 * <p>All elements are permitted, including {@code null}.
 *
 * <p>Memory consistency effects: As with other concurrent
 * collections, actions in a thread prior to placing an object into a
 * {@code CopyOnWriteArrayList}
 * <a href="package-summary.html#MemoryVisibility" target="_blank" rel="external nofollow" ><i>happen-before</i></a>
 * actions subsequent to the access or removal of that element from
 * the {@code CopyOnWriteArrayList} in another thread.
 *
 * @since 1.5
 * @author Doug Lea
 * @param <E> the type of elements held in this list
 */
           

本人google翻译了其中大部分术语,简单翻译了原文,原文大致是这样的:

CopyOnWriteArrayList是线程安全的ArrayList的变种,它里面是通过底层数组的拷贝来实现更改操作。

通常情况下这种实现是昂贵耗时的,但是当遍历操作变化大时,它比一般方案更高效并且当你不能或不愿去同步遍历数组时,同时必须避免并发线程间干扰时更有用。

“快照”形式迭代器方法使用迭代器创建数组状态引用,数组在迭代器生命周期内永远不会被改变,因此,相互干扰是不会发生的,并且迭代器保证了不抛出并发修改异常,从迭代器创建时,它就不会响应List 的添加、移除和更改。迭代器的更改操作将不支持,调用删除、修改、添加等方法会报不支持操作异常。

内存一致性影响:对比其他并发集合,现场保存对象到CopyOnWriteArrayList中的动作发生在其他线程存储、删除元素操作的前面。

分析

类的源码在这里,因为CopyOnWriteArrayList类是普通ArrayList 的一个变异版本,所以与ArrayList的实现大致相同,本类实现List接口。下面2个变量是实现本列表的关键变量。lock用来同步,elements存储数据,类型是最基础父类 Object,可以添加各种类型的对象,而方法中要适配不同的对象,所以使用了泛型(方法参数带T & E)。

  • 关键字volatile的用意是当变量有修改时,所有线程拥有此变量将无效,必须重新获取此变量。
  • 关键字transient,修饰的对象将不被序列化(因为本类集成了Serializable接口);
  • 迭代器,内部类COWIterator实现了ListIterator接口,迭代器只能访问数组中的元素,不能增加、删除、修改元素(数组中元素的相对位置)。
  • 如果对列表进行修改、删除、添加,则这些方法要进行同步检查,全局锁对象lock.
  • 子类表类–COWSubList。如果修改子列表的数组,那么要达到同步效果,方法是获取父列表的lock对象,并对方法进行同步。
final transient Object lock = new Object();
    private transient volatile Object[] elements;
           

总结

  • 线程安全,表示类中的所有方法对数组进行修改,都要进行同步,防止多线程并发修改。
  • 此类有个特别的地方,对数组的修改,都是通过拷贝数组来完成,而不是说在原数组中进行元素移位。另外有个常识—java数组移动比拷贝要慢,因为拷贝是底层实现。
  • 序列化。对数组elements不进行序列化,但是要对数组里面的元素进行序列化。

如果您觉得本篇文章很值得,作者很卖力,欢迎任意打赏

关于CopyOnWriteArrayList的技术理解

继续阅读