CopyOnWriteArrayList为什么不会产生ConcurrentModificationException?
场景
多线程场景下对未正确处理并发的容器进行迭代会抛出异常(ConcurrentModificationException),并发容器(CopyOnWriteArrayList)不会抛出ConcurrentModificationException,并且该类容器也不需要在客户端(调用者)进行加锁或复制。
分析
从CopyOnWriteArrayList源码入手学习该类容器是如何避免ConcurrentModificationException问题的。
CopyOnWriteArrayList类声明了一个Object数组负责元素的存储,一个互斥锁(ReentrantLock)负责保障容器操作的线程安全性。
图示1
一、请求在容器中添加新元素的实现机制
1、获取互斥锁
2、Copy当前元素数组创建新数组,数组内存空间增1
3、添加元素
4、请求容器数据数组内存地址变更为新数组地址
5、释放互斥锁
6、返回结果
图示2
二、容器迭代
1、创建Iterator实现类(内部类)实例,传递容器当前数据数组,初始下标为0
2、返回Iterator实现类(内部类)实例
图示3.1
图示3.2
并发场景下对容器的添加操作是通过在容器内部数据数组的副本来完成的。对容器的迭代使用的是容器原始数据数组因为迭代不会产生修改,因此多个线程可以同时对容器进行迭代,而不会对彼此干扰或影响修改容器的线程。
参考资料
1、Java Concurrency in Practice
2、JDK1.7#java.util.concurrent.CopyOnWriteArrayList<E>