JAVA并发包类|JAVA并发包类 - CopyOnWriteArrayList

说明:以下内容皆属于个人对源码的理解,可能存在歧义、误解、错误及理解不全面的情况,还望指正
一、CopyOnWriteArrayList介绍 CopyOnWriteArrayListArrayList的线程安全变体。底层通过创建数组的新副本来实现并发情况下对数组的更新操作(包括add、set等方法)。这种方式通常是花费代价比较大的,但是当遍历操作远大于更新操作的情况时,这种方式却是更有效的。当然,我们需要避免并发线程之间的干扰。CopyOnWriteArrayList允许添加所有的元素,包括Null
【JAVA并发包类|JAVA并发包类 - CopyOnWriteArrayList】内存一致性效果:与其他并发集合一样,将对象放入CopyOnWriteArrayList之前的线程中的动作发生在另一个线程的CopyOnWriteArrayList中访问或删除该元素之后的操作之前。
二、CopyOnWriteArrayList实现

  • 构造函数
    CopyOnWriteArrayList提供了三个构造函数:默认的构造函数,传入指定集合的构造函数,将指定数组拷贝到CopyOnWriteArrayList的构造函数。目前只了解一下默认构造函数的实现。
    CopyOnWriteArrayList在调用构造函数实例化对象的时候,会创建一个大小为0的数组对象,将CopyOnWriteArrayList底层的数组对象变量指向这个数组。这个对象变量在源码中的定义如下:
/** The array, accessed only via getArray/setArray. */ private transient volatile Object[] array;

底层数组变量array被声明为volatile 类型的,这样在对数组进行遍历的时候获取的都是最新的值。那么并发访问数据遍历的问题就得以解决。
在创建
CopyOnWriteArrayList
对象实例时,同时会创建一个ReentranctLock的实例。
  • add(E e)方法
    调用CopyOnWriteArrayListadd()方法时,首先会用ReentranctLock实例的lock()方法获取锁,然后获取底层数组对象并将底层数组对象拷贝到一个新的数组对象中,将要添加的元素添加到新的数组对象中,最后调用setArray方法,将底层数组对象变量array引向新的数组对象。最后释放锁。
  • iterator()方法
    iterator()方法其实是在内部创建了一个COWIterator实例对象,在该实例对象的内部声明一个数组的快照变量指向CopyOnWriteArrayList的底层数组。然后通过该变量进行对数组元素的遍历操作。与iterator()方法类似的还有sublist()方法。
  • 其它方法
    CopyOnWriteArrayList的其它的更新操作基本与add()方法思想一直,都是先获取锁,在操作完成后将原有数组变量指向新的数组变量。最后在finally块中释放锁。
三、CopyOnWriteArrayList总结 从CopyOnWriteArrayList的实现,可以看出包含了2个思想:读写分离和最终一致性(因为并发访问情况下,某个线程作了元素的更新操作后,array变量尚未指向新数组前,其它线程访问的可能还是原来的数组里的元素)。对于多线程情况下,遍历访问操作远大于更新操作的时候,CopyOnWriteArrayList无疑是最好的选择,省略了我们自己去加同步的操作。但是数组的拷贝是一项费时费资源的操作,对于非多线程或数据变化较多的情况下,还是避免使用。

    推荐阅读