在实现线程安全的侦听器时,我通常想知道哪种类型的Collection
侦听器最好。到目前为止,我已经找到了三个选项。
该标准Observable
使用synchronized
访问一个简单的ArrayList
. 使用监听器的副本是可选的,但据我所知,这是一个好主意,因为它可以防止诸如
- 侦听器在回调中删除自身(
ConcurrentModificationException
- 可以通过索引for
循环以相反的顺序进行迭代以防止这种情况发生) - 在同步块内执行外部代码会阻塞整个事情。
不幸的是,实现不止几行。Collections.synchronizedList()
不需要synchronized
,removeListener
但这并不值得。
class ObservableList {
private final List<Listener> listeners = new ArrayList<Listener>();
public void addListener(Listener listener) {
synchronized (listeners) {
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
}
public void removeListener(Listener listener) {
synchronized (listeners) {
listeners.remove(listener);
}
}
protected void notifyChange() {
Listener[] copyOfListeners;
synchronized (listeners) {
copyOfListeners = listeners.toArray(new Listener[listeners.size()]);
}
// notify w/o synchronization
for (Listener listener : copyOfListeners) {
listener.onChange();
}
}
}
但是其中有些Collection
sjava.util.concurrent
本质上是线程安全的,并且可能更有效,因为我认为它们的内部锁定机制比简单的synchronized
块优化得更好。为每个通知创建一个副本也非常昂贵。
基于CopyOnWriteArrayList
class ObservableCopyOnWrite {
private final CopyOnWriteArrayList<Listener> listeners = new CopyOnWriteArrayList<>();
public void addListener(Listener listener) {
listeners.addIfAbsent(listener);
}
public void removeListener(Listener listener) {
listeners.remove(listener);
}
protected void notifyChange() {
for (Listener listener : listeners) {
listener.onChange();
}
}
}
应该与第一个版本大致相同,但副本较少。添加/删除侦听器不是一个非常频繁的操作,这意味着副本也不应该非常频繁。
我通常使用的版本是基于使用的ConcurrentHashMap
状态,.keySet()
如下Set
所示:
视图的迭代器是一个“弱一致”的迭代器,它永远不会抛出 ConcurrentModificationException,并保证在构造迭代器时遍历元素,并且可以(但不保证)反映构造后的任何修改。
这意味着迭代至少包括在开始迭代时注册的每个监听器,甚至可能包括在迭代期间添加的新监听器。不确定已删除的侦听器。我确实喜欢这个版本,因为它不是复制的,而且实现起来像CopyOnWriteArrayList
.
class ObservableConcurrentSet {
private final Set<Listener> listeners = Collections.newSetFromMap(new ConcurrentHashMap<Listener, Boolean>());
public void addListener(Listener listener) {
listeners.add(listener);
}
public void removeListener(Listener listener) {
listeners.remove(listener);
}
protected void notifyChange() {
for (Listener listener : listeners) {
listener.onChange();
}
}
}
基于实现是ConcurrentHashMap
一个好主意还是我在这里忽略了一些东西?还是有更好Collection
的使用方法?