CopyOnWriteArrayList是ArrayList的一个线程安全的变体,其中所有可变操作(add、set 等等)都是通过对底层数组进行一次新的复制来实现的。 这一般需要很大的开销,但是当遍历操作的数量大大超过可变操作的数量时,这种方法可能比其他替代方法更 有效。
在不能或不想进行同步遍历,但又需要从并发线程中排除冲突时,它也很有用。“快照”风格的迭代器方法在创建迭代器时使用了对数组状态的引用。此数组在迭代器的生存期内不会更改,因此不可能发生冲突,并且迭代器保证不会抛出 ConcurrentModificationException。
创建迭代器以后,迭代器就不会反映列表的添加、移除或者更改。在迭代器上进行的元素更改操作(remove、set 和 add)不受支持。这些方法将抛出 UnsupportedOperationException。 允许使用所有元素,包括 null。
CopyOnWriteArrayList通过将任何对底层数组进行结构性修改的操作变成针对一个新的副本的修改,然后用修改后的副本来替换原来的数组,来实现遍历与修改分离,以保证数组高效的访问效率。
CopyOnWriteArrayList的重要的几个方法:add(int, E)/add(E)/set(int, E)/remove(int)/iterator(),其中前四个是对CopyOnWriteArrayList的结构进行修改,最后一个是对CopyOnWriteArrayList进行遍历。
add()方法的实现很简单,通过加锁保证线程安全,通过Arrays.copyOf根据原数组复制一个新的数组,将要插入的元素插入到新的数组的对应位置,然后将新的数组赋值给array,通过volatile保证内存可见。
set()比add()更新简单,只需要复制一个新的数组,然后更新新的数组的指定位置的元素,然后更新引用即可。
remove()有两种方式,根据指定位置删除以及指定元素删除两种方式。
iterator()
这里的iterator()只是很简单的迭代器,内部将remove/set/add三个修改操作进行了限制,因为这里的迭代器不能修改集合,代码就不细看了。注意到iterator并没有加锁,因为iterator所访问的数组是不会变的,就算有其他线程对集合进行修改。
使用场景
CopyOnWriteArrayList适合读多写少的场景。通过空间换时间的方式来提高读的效率并保证写的安全性。