有一个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数据进行迭代时,什么是避免由于meventmap数据的更改而导致崩溃的最佳方法。
4条答案
按热度按时间tuwxkamq1#
您的问题是,您希望在使用隐式迭代器遍历元素时将其从列表中移除。
您可以通过显式使用迭代器来解决这个问题:
u0njafvf2#
另一种方法是使用java内置的eventlistenerlist类https://docs.oracle.com/javase/8/docs/api/index.html?javax/swing/event/eventlistenerlist.html. 尽管它在javax.swing包中,但我发现它对于一般的事件通知非常有用。它由数组支持,按类管理侦听器,并且不引发并发mod异常。触发事件的方式如下:
wfveoks03#
在中创建列表的安全副本
dispatchEvent
在循环之前,比如:但到目前为止,听众最好的方法是
CopyOnWriteArrayList
管理侦听器列表,因为您不经常修改它,而且它已经是线程安全的,因此您不需要任何侦听器synchronized blocks
不再。pkln4tw64#
选择的方法是在侦听器对象“isremoved”中添加一个标志。所以removelistener只会标记这个监听器已经被移动,但它会一直保留在列表中,直到分派时间,而分派时间只会分派给那些没有标记的监听器。所有标记的将在调度循环后从列表中删除。