美文网首页述术
并发包系列二—— CopyOnWriteArrayList

并发包系列二—— CopyOnWriteArrayList

作者: FlySheep_ly | 来源:发表于2017-03-26 17:10 被阅读16次

本系列文章所描述的所有类或接口都是基于 JDK 1.7的源码,在其它 JDK 的实现方式中可能会有所不同。

一、简单介绍

CopyOnWriteArrayList 是一个线程安全、并且在读操作时无锁的 ArrayList,其具体实现方法如下。

二、CopyOnWriteArrayList()

和 ArrayList 不同,此步的做法为创建一个大小为 0 的数组。

    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }

    final void setArray(Object[] a) {
        array = a;
    }

三、add(E)

add 方法并没有加上 synchronized 关键字,它通过使用 ReentrantLock 来保证线程安全。
  除了使用 ReentrantLock 来保证线程安全外,此处和 ArrayList 的不同是每次都会创建一个新的 Object 数组,此数组的大小为当前数组大小加 1,将之前数组中的内容复制到新的数组中,并将新增加的对象放入数组末尾,最后做引用切换将新创建的数组对象赋值给全局的数组对象。

四、remove(E)

和 add 方法一样,此方法也通过 ReentrantLock 来保证其线程安全,但它和 ArrayList 删除元素采用的方式并不一样。
  首先创建一个比当前数组小 1 的数组,遍历新数组,如找到 equals 或均为 null 的元素,则将之后的元素全部赋值给新的数组对象,并做引用切换,返回 true;如未找到,则将当前的元素赋值给新的数组对象,最后特殊处理数组中的最后一个元素,如最后一个元素等于要删除的元素,则将当前数组对象赋值为新创建的数组对象,完成删除操作,如最后一个元素也不等于要删除的元素,那么返回 false。
  此方法和 ArrayList 除了锁不同外,最大的不同在于其复制过程并没有调用 System 的 arrayCopy 来完成,理论上来说会导致性能有一定下降。

五、get(int)

此方法非常简单,直接获取当前数组对应位置的元素,这种方法是没有加锁保护的,因此可能会出现读到脏数据的现象。但相对而言,性能会非常高,对于写少读多且脏数据影响不大的场景而言,CopyOnWriteArrayList 是不错的选择。

六、iterator()

调用 iterator 方法后创建一个新的 COWIterator 对象实例,并保存了一个当前数组的快照,在调用 next 遍历时则仅对此快照数组进行遍历,因此遍历 CopyOnWriteArrayList 时不会抛出 ConcurrentModificatiedException。
  从以上的分析可见,CopyOnWriteArrayList 基于 ReentrantLock 保证了增加元素和删除元素动作的互斥。在读上没有做任何锁操作,这样就保证了读的性能,带来的副作用是有些时候可能会读取到脏数据。

相关文章

网友评论

    本文标题:并发包系列二—— CopyOnWriteArrayList

    本文链接:https://www.haomeiwen.com/subject/bbskottx.html