Java 迭代过程中的HashMap插入
我在映射中维护可变数量的事件侦听器:Java 迭代过程中的HashMap插入,java,multithreading,events,collections,Java,Multithreading,Events,Collections,我在映射中维护可变数量的事件侦听器: private final Map<String, EventListener> eventListeners = new ConcurrentHashMap<>(); 每次发生事件时,我都会迭代所有侦听器并激发它们: eventListeners.forEach((name, listener) -> { listener.handle(event); }); 该环境是单线程的,有趣的是事件侦听器实现可能会在触发时
private final Map<String, EventListener> eventListeners = new ConcurrentHashMap<>();
每次发生事件时,我都会迭代所有侦听器并激发它们:
eventListeners.forEach((name, listener) -> {
listener.handle(event);
});
该环境是单线程的,有趣的是事件侦听器实现可能会在触发时插入另一个事件侦听器
对于“普通”HashMap,这显然会导致ConcurrentModificationException
,这就是我使用ConcurrentHashMap
的原因
我看到的情况是,由另一个事件侦听器插入的事件侦听器可能会被同一个事件触发,因为它是在迭代过程中插入的,我认为这是ConcurrentHashMap
的预期行为
然而,我不希望发生这种情况,所以我想推迟事件侦听器的任何插入,直到对所有侦听器的迭代完成
因此,我引入了一个线程
,它在使用倒计时闩锁插入侦听器之前等待迭代完成:
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();
它按预期工作,但我想知道这是否是一个很好的解决方案,是否有更优雅的方法来实现我的需求。我还没有完全分析您的问题,但通常的方法是使用CopyOnWriteArraYList
public class CopyOnWrite {
private CopyOnWriteArrayList<ActionListener> list =
new CopyOnWriteArrayList<>();
public void fireListeners() {
for( ActionListener el : list )
el.actionPerformed( new ActionEvent( this, 0, "Hi" ) );
}
public void addListener( ActionListener al ) {
list.add( al );
}
}
公共类CopyOnWrite{
私有CopyOnWriteArrayList
CopyOnWriteArrayList是由
写时复制数组。此实现与
CopyOnWriteArraySet。无需同步,即使在
迭代,并保证迭代器永远不会抛出
ConcurrentModificationException。此实现非常适合于
维护不经常更改的事件处理程序列表,以及
遍历非常频繁,而且可能非常耗时
我还没有完全分析您的问题,但通常的方法是使用CopyOnWriteArraYList
public class CopyOnWrite {
private CopyOnWriteArrayList<ActionListener> list =
new CopyOnWriteArrayList<>();
public void fireListeners() {
for( ActionListener el : list )
el.actionPerformed( new ActionEvent( this, 0, "Hi" ) );
}
public void addListener( ActionListener al ) {
list.add( al );
}
}
公共类CopyOnWrite{
私有CopyOnWriteArrayList
CopyOnWriteArrayList是由
写时复制数组。此实现与
CopyOnWriteArraySet。无需同步,即使在
迭代,并保证迭代器永远不会抛出
ConcurrentModificationException。此实现非常适合于
维护不经常更改的事件处理程序列表,以及
遍历非常频繁,而且可能非常耗时
我还没有完全分析您的问题,但通常的方法是使用CopyOnWriteArraYList
public class CopyOnWrite {
private CopyOnWriteArrayList<ActionListener> list =
new CopyOnWriteArrayList<>();
public void fireListeners() {
for( ActionListener el : list )
el.actionPerformed( new ActionEvent( this, 0, "Hi" ) );
}
public void addListener( ActionListener al ) {
list.add( al );
}
}
公共类CopyOnWrite{
私有CopyOnWriteArrayList
CopyOnWriteArrayList是由
写时复制数组。此实现与
CopyOnWriteArraySet。无需同步,即使在
迭代,并保证迭代器永远不会抛出
ConcurrentModificationException。此实现非常适合于
维护不经常更改的事件处理程序列表,以及
遍历非常频繁,而且可能非常耗时
我还没有完全分析您的问题,但通常的方法是使用CopyOnWriteArraYList
public class CopyOnWrite {
private CopyOnWriteArrayList<ActionListener> list =
new CopyOnWriteArrayList<>();
public void fireListeners() {
for( ActionListener el : list )
el.actionPerformed( new ActionEvent( this, 0, "Hi" ) );
}
public void addListener( ActionListener al ) {
list.add( al );
}
}
公共类CopyOnWrite{
私有CopyOnWriteArrayList
CopyOnWriteArrayList是由
写时复制数组。此实现与
CopyOnWriteArraySet。无需同步,即使在
迭代,并保证迭代器永远不会抛出
ConcurrentModificationException。此实现非常适合于
维护不经常更改的事件处理程序列表,以及
遍历非常频繁,而且可能非常耗时
如果您的解决方案允许您实现线程
,则可能出现问题
您可以避免在原始地图上迭代,而在其副本上迭代
比如:
Map<String, EventListener> eventListenerCopy = new HashMap<>(eventListeners);
eventListenerCopy.forEach((name, listener) -> {
listener.handle(event);
});
Map eventListenerCopy=newhashmap(eventListeners);
eventListenerCopy.forEach((名称,侦听器)->{
句柄(事件);
});
因此,如果您的句柄
事件将添加新事件,则副本将无法看到它,而副本将迭代所有原始事件
或者,如果强制某个特定方法添加新的侦听器,则可以创建一个临时列表,保存所有新的侦听器,并在迭代完成时检查此新列表,如果存在,则将新的侦听器放在原始列表中(如果不希望每次都创建映射的副本,则更好).如果您的解决方案允许您实现线程
,则可能出现问题
您可以避免在原始地图上迭代,而在其副本上迭代
比如:
Map<String, EventListener> eventListenerCopy = new HashMap<>(eventListeners);
eventListenerCopy.forEach((name, listener) -> {
listener.handle(event);
});
Map eventListenerCopy=newhashmap(eventListeners);
eventListenerCopy.forEach((名称,侦听器)->{
句柄(事件);
});
因此,如果您的句柄
事件将添加新事件,则副本将无法看到它,而副本将迭代所有原始事件
或者,如果强制某个特定方法添加新的侦听器,则可以创建一个临时列表,保存所有新的侦听器,并在迭代完成时检查此新列表,如果存在,则将新的侦听器放在原始列表中(如果不希望每次都创建映射的副本,则更好).如果您的解决方案允许您实现线程
,则可能出现问题
您可以避免在原始地图上迭代,而在其副本上迭代
比如:
Map<String, EventListener> eventListenerCopy = new HashMap<>(eventListeners);
eventListenerCopy.forEach((name, listener) -> {
listener.handle(event);
});
Map eventListenerCopy=newhashmap(eventListeners);
eventListenerCopy.forEach((名称,侦听器)->{
句柄(事件);
});
因此,如果您的句柄
事件将添加新事件,则副本将无法看到它,而副本将迭代所有原始事件
或者,如果强制指定