简介

        本文介绍Java的CopyOnWriteArrayList的原理。

        CopyOnWriteArrayList是JUC的一个并发容器,它是个线程安全的ArrayList。

        读操作无锁,写操作则通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可以称这种容器为"写时复制器",JUC中类似的容器还有CopyOnWriteSet(底层是基于CopyOnWriteArrayList实现)。

        Write的时候总是要Copy(将原来array复制到新的array,修改后,将引用指向新数组)。任何可变的操作(add、set、remove等)都通过ReentrantLock 控制并发。

优缺点

优点

  1. 读操作性能很高
  1. 因为读操作无需任何同步措施,适用于读多写少的并发场景。
  1. 不会抛出ConcurrentModificationException异常
  1. list在遍历时,若中途有别的线程对list容器进行修改,则会抛出ConcurrentModificationException异常。而CopyOnWriteArrayList由于其"读写分离"的思想,遍历和修改操作分别作用在不同的list容器,所以在使用迭代器进行遍历时候,也就不会抛出ConcurrentModificationException异常了

缺点

  1. 内存占用大
  1. 每次写操作都将原容器拷贝一份,数据量大时,对内存压力较大,可能会引起频繁GC;
  1. 无法保证实时性
  1. Vector对于读写操作均加锁同步,可以保证读和写的强一致性
  2. CopyOnWriteArrayList写和读分别作用在新老不同容器上,在写操作执行过程中,读不会阻塞但读取到的却是老容器的数据。
  1. 大量写操作性能极差

功能的限制

迭代器没有remove功能

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
list.add("aaa");
list.add("bbb");
list.add("ccc");

Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
String next = iterator.next();
iterator.remove();
}

结果

Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.concurrent.CopyOnWriteArrayList$COWIterator.remove(CopyOnWriteArrayList.java:1178)
at ConcurrentSet.main(ConcurrentSet.java:47)

不支持Collections.sort(xx, yy)

会报异常:java.lang.UnsupportedOperationException

解决方案

List temp = Arrays.asList(copyOnWriteArrayList.toArray());
Collections.sort(temp);
copyOnwriteArrayList.clear();
copyOnWriteArrayList.addAll(temp);