我正在测试我正在为项目编写的事件系统。在上述项目和测试中,我不接触线程。从字面上看,我不创建线程或对线程做任何事情。但是,我收到了ConcurrentModificationException。
我知道还有其他情况可能引发此异常。来自CME JavaDoc:
请注意,此异常并不总是表示对象具有 由另一个线程同时修改。如果是单线程 发出一系列违反合同的方法调用 一个对象,该对象可能抛出此异常。
这是我的测试代码:
TestListener test = new TestListener();
Assert.assertTrue(evtMgr.register(test));
Assert.assertFalse(testBool);
evtMgr.fire(new QuestStartEvent(null));
Assert.assertTrue(testBool);
testBool = false;
evtMgr.unregister(test);
evtMgr.fire(new QuestStartEvent(null));
Assert.assertFalse(testBool);
EventManager
看起来像这样:
public boolean register(Listener listener) {
return listeners.add(new ListenerHandle(listener));
}
public void unregister(Listener listener) {
listeners.stream().filter((l) -> l.getListener() == listener)
.forEach(listeners::remove);
}
public <T extends Event> T fire(T event) {
listeners.forEach((listener) -> listener.handle(event));
return event;
}
ConcurrentModificationException位于.forEach(listeners::remove);
ListenerHandle
看起来像这样:
private final Listener listener;
private final Map<Class<? extends Event>, Set<MethodHandle>> eventHandlers;
public ListenerHandle(Listener listener) {
this.listener = listener;
this.eventHandlers = new HashMap<>();
for (Method meth : listener.getClass().getDeclaredMethods()) {
EventHandler eh = meth.getAnnotation(EventHandler.class);
if (eh == null || meth.getParameterCount() != 1) {
continue;
}
Class<?> parameter = meth.getParameterTypes()[0];
if (!Event.class.isAssignableFrom(parameter)) {
continue;
}
Class<? extends Event> evtClass = parameter.asSubclass(Event.class);
MethodHandle handle = MethodHandles.lookup().unreflect(meth);
Set<MethodHandle> handlers = eventHandlers.get(evtClass);
if (handlers == null) {
handlers = new HashSet<>();
eventHandlers.put(evtClass, handlers);
}
handlers.add(handle);
}
}
public void handle(Event event) {
Class<? extends Event> clazz = event.getClass();
Set<MethodHandle> handles = eventHandlers.get(clazz);
if (handles == null || handles.isEmpty()) {
return;
}
for (MethodHandle handle : handles) {
handle.invoke(listener, event);
}
}
(为了便于阅读,尝试捕获)
堆栈跟踪:
java.util.ConcurrentModificationException
at java.util.HashMap$KeySpliterator.forEachRemaining(HashMap.java:1545)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
at EventManager.unregister(EventManager.java:54)
(第54行是.forEach(listeners::remove);
)
答案 0 :(得分:0)
您正在获得并发修改例外,因为您在迭代它的同时修改了集合。而不是
listeners.stream().filter((l) -> l.getListener() == listener)
.forEach(listeners::remove);
你应该使用传统的基于Iterator
的习惯用法并在迭代器而不是集合上调用remove()
,或者首先迭代以收集要删除的对象集,然后在一个对象中删除它们在初始迭代完成后再进行:
listeners.removeAll(
listeners.stream().filter((l) -> l.getListener() == listener)
.collect(Collectors.toList()));
迭代器方法可能更有效。