HashMap在迭代过程中插入

托斯滕·罗默(TorstenRömer)

我在地图中维护了可变数量的事件侦听器:

private final Map<String, EventListener> eventListeners = new ConcurrentHashMap<>();

使用地图的类具有添加事件侦听器的方法:

public void addEventListener(final String name, final EventListener listener) {
    eventListeners.put(name, listener);
}

每次发生事件时,我都会遍历所有侦听器并将其解雇:

eventListeners.forEach((name, listener) -> {
    listener.handle(event);
});

该环境是单线程的,引起关注的部分是事件侦听器实现在被激发时可能会插入另一个事件侦听器。

对于“普通” HashMap,这显然会导致a ConcurrentModificationException,这就是为什么我使用a ConcurrentHashMap

我看到的是,由于另一个事件侦听器插入的事件侦听器是在迭代过程中插入的,因此同一事件可能会触发该事件侦听器,我认为这是的预期行为ConcurrentHashMap

但是,我不希望发生这种情况,因此我想将事件侦听器的任何插入推迟到所有侦听器的迭代完成为止。

因此,我引入了一个Thread等待迭代完成的迭代,然后再使用a插入侦听器CountDownLatch

public void addEventListener(final String name, final EventListener listener) {
    new Thread(() -> {
        try {
            latch.await();
        } catch (InterruptedException e) {}
        eventListeners.put(name, listener);
    }).start();
}

以及所有侦听器的迭代:

latch = new CountDownLatch(1);
eventListeners.forEach((name, listener) -> {
    listener.handle(event);
});
latch.countDown();

它可以按预期工作,但是我不知道这是否是一个好的解决方案,是否还有更优雅的方法来实现我所需要的。

马可·阿西尔诺

如果您的解决方案允许您实施Thread某件事,那可能是错误的。

您可以避免对原始地图进行迭代并对其副本进行迭代。

所以像这样:

Map<String, EventListener> eventListenerCopy = new HashMap<>(eventListeners);
eventListenerCopy.forEach((name, listener) -> {
  listener.handle(event);
});

因此,如果您的handle事件将添加新事件,则该副本将不会遍历所有原始事件的副本被看到。

或者,如果您强制使用特定方法添加新的侦听器,则可以创建一个临时列表,其中将保存所有新的侦听器,并在迭代完成时检查此新列表,如果存在,则将新的侦听器放入原始列表中(这可能如果您不想每次都创建地图副本,则更好。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章