在事件总线中避免ConcurrentModificationException

时间:2014-04-02 16:20:17

标签: java concurrency crash hashset

我有一个EventBus系统,客户可以随时加入/离开。

EventBus正在使用HashSet跟踪客户端,并在广播消息时将其发送给所有已注册的客户。

// clients - HashSet of clients.
// message - event object to be delivered

for (Object client : clients) {
    sendTo(client, message);    
}

我知道这是低效的,但我不知道如何改进它。

问题是客户端可以通过断开与总线的连接来对某些事件做出反应。让我们把它当作一个事实,它必须是可能的。

他们称这种方法为:

public void unsubscribe(Object client)
{
    clients.remove(client);
}

然后,当然,我收到一个错误:

[!E!] java.util.ConcurrentModificationException
[!E!]   at java.util.LinkedHashMap$LinkedHashIterator.nextEntry(LinkedHashMap.java:390)
[!E!]   at java.util.LinkedHashMap$KeyIterator.next(LinkedHashMap.java:401)
[!E!]   at mightypork.utils.control.bus.EventChannel.doBroadcast(EventChannel.java:56)
[!E!]   at mightypork.utils.control.bus.EventChannel.broadcast(EventChannel.java:48)
[!E!]   at mightypork.utils.control.bus.EventBus.broadcast(EventBus.java:82)
[!E!]   at mightypork.rogue.App.shutdown(App.java:133)
[!E!]   at mightypork.rogue.App.start(App.java:87)
[!E!]   at mightypork.rogue.App.main(App.java:72)
[!E!] 

不酷,对吧?

有什么想法解决这个问题吗?

4 个答案:

答案 0 :(得分:1)

您可以使用LinkedList

使用LinkedList,您可以使用其迭代器循环遍历它,并使用相同的迭代器从LinkedList中删除该对象。

示例:

List<Client> clients = new LinkedList<>();
Iterator it = clients.iterator();

while (it.hasNext())
{
  Client client = (Client) it.next();
  sendTo(client, message);
  it.remove();
}

答案 1 :(得分:0)

最简单的方法是标记应删除的客户端,然后在&#34; safe&#34;中删除它们。的地方。

public void unsubscribe(Object client)
{
    clientsToRemove.add(client);
}

在代码中这样:

for (Object client : clients) {
    sendTo(client, message);    
}
client.removeAll(clientsToRemove);

答案 2 :(得分:0)

如果使用Iterator,则可以在迭代期间修改集合。有关更多信息,请参阅集合界面教程:http://docs.oracle.com/javase/tutorial/collections/interfaces/collection.html

答案 3 :(得分:0)

我已使用CopyOnWriteArraySet代替HashSet解决了这个问题。

无论如何,感谢您的建议,它可以帮助其他人。