# 《并发设计模式》第07章-不可变模式-JDK中的等效不可变类CopyOnWriteArrayList

作者:冰河
星球:http://m6z.cn/6aeFbs (opens new window)
博客:https://binghe.gitcode.host (opens new window)
文章汇总:https://binghe.gitcode.host/md/all/all.html (opens new window)
源码获取地址:https://t.zsxq.com/0dhvFs5oR (opens new window)

沉淀,成长,突破,帮助他人,成就自我。

  • 本章难度:★★☆☆☆
  • 本章重点:掌握CopyOnWriteArrayList的核心原理,重点理解CopyOnWriteArrayList为何是等效不可变类,而不是不可变类。重点掌握写时复制技术,能够在合适的场景将CopyOnWriteArrayList灵活应用的自身实际项目中。

大家好,我是冰河~~

在前面讲解不可变模式实现的案例时,老王随口问了小菜一句:“JDK中就有算是等效使用不可变模式实现的类,感兴趣的话,明天我再给你讲”,这对于渴望技术和知识的小菜来说,无疑是一件好事儿,于是小菜便等着老王给其讲解JDK中的等效不可变类。

# 一、故事背景

小菜从老王口中得出了JDK中存在等效不可变类,但是心里有两个疑惑:一个是什么是等效不可变类,而是JDK中哪些类是等效不可变类。下班回家后,小菜针对这两个问题也是百思不得其解,在网上搜索答案,也没搜出个所以然来。

# 二、寻求帮助

第2天小菜还是带着疑问早早的来到了公司(小菜肯定拿全勤奖),正当其坐在工位上不断在网上搜索自己想要的答案时,听到了老王的声音:“昨天那个需求已经实现提交测试了,回头看看测试结果,是否交付验收”。小菜回过头来,看到老王跟产品经理边说边走到了工位上。

这时,小菜起身对老王说道:“老大,有时间吗?给我讲讲昨天说的等效不可变类呀,我现在有两个疑问:一个是什么是等效不可变类,一个是JDK中哪些是等效不可变类”。

老王听后说:“好的,我们还是去会议室说吧”。

于是,二人起身向会议室走去。

# 三、一探究竟

“等效不可变对象的意思就是创建出来的对象基本符合不可变的原则和特征,但是还是存在部分差异,在某些情况下这些对象的内部状态可能会发生变化”,老王来到会议室,边打开电脑边说,“这么说的话可能有点不好理解,我们结合JDK中的CopyOnWriteArrayList类进行分析,其实CopyOnWriteArrayList类就是一个典型的等效不可变类”。

“我了解过CopyOnWriteArrayList类,在添加、修改和删除元素时,不会在原来的数组上操作,会创建一个新的数组,添加、修改和删除完元素时,会将引用指向新的数组对象。遍历和读取元素时,会在原来的数组上操作”,小菜说道。

“对,是的,对CopyOnWriteArrayList的读操作会在原来的数组上进行,写操作会创建一个新的数组对象,今天我们就好好讲讲这个CopyOnWriteArrayList类”,老王边说边打开了CopyOnWriteArrayList的源码,继续说道:“来看下CopyOnWriteArrayList的源码,我们结合源码说它为何是一个等效不可变类”。

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = 8673264195747942595L;

    /** The lock protecting all mutators */
    final transient ReentrantLock lock = new ReentrantLock();

    /** The array, accessed only via getArray/setArray. */
    private transient volatile Object[] array;

    /**
     * Gets the array.  Non-private so as to also be accessible
     * from CopyOnWriteArraySet class.
     */
    final Object[] getArray() {
        return array;
    }

    /**
     * Sets the array.
     */
    final void setArray(Object[] a) {
        array = a;
    }
    /**************暂时忽略其他代码***************/
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

“这里,可以看到,在CopyOnWriteArrayList类中,会维护一个Object类型的数组array,并且会通过getArray()和setArray()方法来访问array数组”,老王打开CopyOnWriteArrayList类的源码结合上述代码片段对小菜说。

“接下来,咱们再来看看CopyOnWriteArrayList类遍历元素的方法,这个方法就是iterator()方法”。

public Iterator<E> iterator() {
	return new COWIterator<E>(getArray(), 0);
}
1
2
3

“可以看到,在iterator()方法遍历数组时,会调用getArray()方法获取CopyOnWriteArrayList本身的array数组,基于这个array数组遍历其中的元素”。

“我们再来看看向CopyOnWriteArrayList中添加元素的方法”,说着老王便定位到如下的代码片段。

public boolean add(E e) {
    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();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 查看全文

加入冰河技术 (opens new window)知识星球,解锁完整技术文章与完整代码