农场主的黑科技.

并发操作合集-8.并发容器:CopyOnWriteArrayList

字数统计: 1k阅读时长: 6 min
2018/11/04 Share

🍤 并发操作合集系列 目录

🍕 并发操作合集系列 源代码

这一篇文章也会介绍一个并发容器,CopyOnWriteArrayList。

CopyOnWriteArrayList

Copy-On-Write简称COW,是一种用于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器非常有用,可以在非常多的并发场景中使用到。

什么是CopyOnWrite容器

CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。

这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

CopyOnWrite的应用场景

CopyOnWrite并发容器用于读多写少的并发场景。比如白名单,黑名单,商品类目的访问和更新。此时需要注意的是:

  1. 减少扩容开销。根据实际需要,初始化CopyOnWriteMap的大小,避免写时CopyOnWriteMap扩容的开销。
  2. 使用批量添加。因为每次添加,容器每次都会进行复制,所以减少添加次数,可以减少容器的复制次数。如使用上面代码里的addBlackList方法。

CopyOnWrite的缺点

  • 内存占用问题。因为CopyOnWrite的写时复制机制,所以在进行写操作的时候,内存里会同时驻扎两个对象的内存,旧的对象和新写入的对象。
  • 数据一致性问题。CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。所以如果你希望写入的的数据,马上能读到,请不要使用CopyOnWrite容器。

示例程序

CopyOnWriteArrayList的添加操作

之前我们介绍到CopyOnWrite是在元素发生改变时,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。

我们取出添加前和添加后的iterator进行对比:

1
2
3
4
5
6
7
8
9
CopyOnWriteArrayList<Integer> numbers
= new CopyOnWriteArrayList<>(Arrays.asList(1, 2, 3, 4));
Iterator<Integer> preIt = numbers.iterator();
numbers.add(9);
Iterator<Integer> newIt = numbers.iterator();

preIt.forEachRemaining(System.out::print); //输出1234
System.out.println();
newIt.forEachRemaining(System.out::print); //输出12349

添加后的iterator应该包含了新值,而添加前的则不包含,可以看出是两个容器对象。所以CopyOnWriteArrayList发生修改,也能被安全迭代。如果在上面程序中用ArrayList代替CopyOnWriteArrayList,则会抛出ConcurrentModificationException

不能在移除迭代器中移除元素

CopyOnWriteArrayList可以安全迭代过期的了的迭代器,但iterator.remove()是无法使用的,会抛出UnsupportedOperationException异常。

1
2
3
4
5
6
7
8
9
CopyOnWriteArrayList<Integer> numbers
= new CopyOnWriteArrayList<>(Arrays.asList(1, 2, 3, 4));
Iterator<Integer> it = numbers.iterator();
try {
while (it.hasNext())
it.remove();
} catch (UnsupportedOperationException e){
System.out.println("抛出了UnsupportedOperationException");
}

Reference

聊聊并发-Java中的Copy-On-Write容器

Guide to CopyOnWriteArrayList

CATALOG
  1. 1. CopyOnWriteArrayList
    1. 1.1. 什么是CopyOnWrite容器
    2. 1.2. CopyOnWrite的应用场景
    3. 1.3. CopyOnWrite的缺点
  2. 2. 示例程序
    1. 2.1. CopyOnWriteArrayList的添加操作
    2. 2.2. 不能在移除迭代器中移除元素
  3. 3. Reference