![](/img/trans.png)
[英]How to Avoid ConcurrentModificationException in that case?
[英]how to avoid ConcurrentModificationException for listen once listener case
具有一个mEventMap来保存不同事件的侦听器,并具有addListener()来将eventlistener,removeListener()和dispatchEvent()注册到已注册的侦听器。
public void addListener(EventListener listener) {
synchronized (mEventMap) {
List<WeakReference<EventListener<Event>>> listeners = mEventMap.get(listener.mEventClass);
WeakReference<EventListener<Event>> listenerRef = new WeakReference<>(
(EventListener<Event>) listener)
…
listeners.add(listenerRef);
…
}
}
public void removeListener(EventListener listener) {
synchronized (mEventMap) {
List<WeakReference<EventListener<Event>>> listeners = mEventMap.get(listener.mEventClass);
…
if (contains(listeners, listener)) {
doRemove(listeners, listener);
}
…
}
}
public boolean dispatchEvent(Event event) {
synchronized (mEventMap) {
List<WeakReference<EventListener<Event>>> listeners = mEventMap.get(event.getClass());
ListIterator<WeakReference<EventListener<Event>>> listenerIterator = listeners.listIterator(listeners.size());
…
while (listenerIterator.hasPrevious()) {
WeakReference<EventListener<Event>> listenerItem = listenerIterator.previous();
EventListener<Event> listenerRef = listenerItem.get();
if (listenerRef != null) {
listenerRef.onEvent(event);
} else {
listenerIterator.remove();
}
}
…
}
用例
EventListener<Event> mEventListener = new EventListener<Event>(
Event.class) {
@Override
public boolean onEvent(Event event) {
eMgr.removeListener(mEventListener);
// do something
}
};
addEventListener(mEventListener);
在dispatchEvent()中,当它在循环中时,将调用removeListener(),并在listenerItem = listenerIterator.previous();处引起ConcurrentModificationException。
问题:避免在有人迭代mEventMap数据时导致崩溃的最佳方法是什么?
在循环之前,在dispatchEvent
创建列表的安全副本,如下所示:
List<WeakReference<EventListener<Event>>> listeners
= new ArrayList<>(mEventMap.get(event.getClass()));
但是到目前为止,对于侦听器而言,最好的方法是使用CopyOnWriteArrayList
来管理侦听器列表,因为您不必对其进行过多修改,并且它已经是线程安全的,因此您不再需要任何synchronized blocks
。
您的问题是您想从列表中删除一个元素, 同时使用隐式迭代器遍历该元素。
您可以通过显式使用迭代器来解决此问题:
for (Iterator<EventListener> it = list.iterator(); it.hasNext(); ) {
EventListener el = it.next();
it.remove();
}
选择的方法是在侦听器对象“ isRemoved”中添加一个标志。 因此,removeListener将仅标记此侦听器已移动,但它将一直保留在列表中,直到分配时间为止(仅将分配给未标记的侦听器)。 在调度循环之后,所有标记的对象将从列表中删除。
另一种方法是使用Java内置的EventListenerList类https://docs.oracle.com/javase/8/docs/api/index.html?javax/swing/event/EventListenerList.html 。 尽管它位于javax.swing包中,但我发现它通常对于事件通知非常有用。 它由数组支持,按类管理侦听器,并且不会引发并发的mod异常。 触发事件是这样完成的:
Object[] listeners = listenerList.getListenerList();
for(int i = listeners.length - 2; i >= 0; i -= 2){
if(listeners[i] == YourListenerClass.class){
((YourListenerClass)listeners[i + 1]).yourListenerMethod(yourEvent);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.