在 Doug Lea 的 Concurrent Programming in Java一书的第 2 章第 2.4.4 节(请参阅 参考资料)中,对 copy-on-write 模式作了最好的描述。实质上,这个模式声明了,为了维护对象的一致性快照,要依靠不可变性(immutability)来消除在协调读取不同的但是相关的属性时需要的同步。对于集合,这意味着如果有大量的读(即get()
) 和迭代,不必同步操作以照顾偶尔的写(即 add()
)调用。对于新的 CopyOnWriteArrayList
和CopyOnWriteArraySet
类,所有可变的(mutable)操作都首先取得后台数组的副本,对副本进行更改,然后替换副本。这种做法保证了在遍历自身更改的集合时,永远不会抛出ConcurrentModificationException
。遍历集合会用原来的集合完成,而在以后的操作中使用更新后的集合。
集合copy副本,写副本,读取当前集合,后续操作中副本替换当前集合。保证读和写不冲突。
这些新的集合, CopyOnWriteArrayList
和 CopyOnWriteArraySet
,最适合于读操作通常大大超过写操作的情况。一个最常提到的例子是使用监听器列表。已经说过,Swing 组件还没有改为使用新的集合。相反,它们继续使用javax.swing.event.EventListenerList
来维护它们的监听器列表。
如清单 6 所示,集合的使用与它们的非 copy-on-write 替代物完全一样。只是创建集合并在其中加入或者删除元素。即使对象加入到了集合中,原来的 Iterator
也可以进行,继续遍历原来集合中的项。
/**
* 注册一个事件监听者
* @param listener
*/
private void registerEventListener(String eventType, EventListener listener) {
Set<EventListener> set = listeners.get(eventType);
if (set == null) {
set = new CopyOnWriteArraySet<EventListener>();
listeners.put(eventType, set);
}
set.add(listener);
if(!EventContext.dispatchers.contains(this)) {
EventContext.dispatchers.add(this);
}
}