ArrayList
的一个线程安全的变体,其中所有可变操作(
add
、
set
等等)都是通过对底层数组进行一次新的复制来实现的。
这一般需要很大的开销,但是当遍历操作的数量大大超过可变操作的数量时,这种方法可能比其他替代方法更 有效。在不能或不想进行同步遍历,但又需要从并发线程中排除冲突时,它也很有用。“快照”风格的迭代器方法在创建迭代器时使用了对数组状态的引用。此数组在迭代器的生存期内不会更改,因此不可能发生冲突,并且迭代器保证不会抛出 ConcurrentModificationException。创建迭代器以后,迭代器就不会反映列表的添加、移除或者更改。在迭代器上进行的元素更改操作(remove、set 和 add)不受支持。这些方法将抛出 UnsupportedOperationException。
2、内存一致性效果:当存在其他并发 collection 时,将对象放入 CopyOnWriteArrayList
之前的线程中的操作 happen-before 随后通过另一线程从CopyOnWriteArrayList
中访问或移除该元素的操作。
3、使用场景:读多,写少,遍历操作不需要进行同步,读写分离。类似一些系统常量的缓存等。
4、源码构造过程:
privatevolatile transient Object[] array;//定义为volatile引用,可以保证数组引用的可见性
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();//volatile的读
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);//生成新的数组
newElements[len] = e;
setArray(newElements);//volatile的写,保证了对新数组的数据被写入主内存,保证了其他线程读取最新数据
return true;
} finally {
lock.unlock();
}
}
final Object[] getArray() {
return array;
}
public E get(int index) {
return (E)(getArray()[index]);//volatile的读,保证了最近写入的对读可见,保证了读取最新的数据
}
--------------------------------------下面是迭代-------------------------------------
private final Object[] snapshot;
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;//对存储数组的引用快照,下面的遍历都是对该变量的操作。该变量是final类型,其他线程写入或者删除元素对该遍历无关。
}
public boolean hasNext() {
return cursor < snapshot.length;
}
public boolean hasPrevious() {
return cursor > 0;
}
public E next() {
if (! hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
}