Question

I have a EventBus system which clients can at will join / leave.

The EventBus is using HashSet to keep track of clients, and when a message is broadcasted, it's delivered to all registered clients.

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

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

I know this is inefficient, but I don't know how to improve it.

The problem is that the clients can react to some events by disconnecting from the bus. Let's take it as a fact, it must be possible.

They call this method:

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

And then, of course, I get an error:

[!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!] 

Not cool, right?

Any idea to fix this?

Was it helpful?

Solution 2

The easiest way is mark clients which should be removed and then remove them in "safe" place.

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

And in code something like this :

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

OTHER TIPS

You can use a LinkedList for this.

With a LinkedList, you can use its iterator to loop through it, and use that same iterator to remove the object from the LinkedList.

Example:

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

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

If you use an Iterator, you can modify a collection during iteration. For more information, see The Collection Interface tutorial: http://docs.oracle.com/javase/tutorial/collections/interfaces/collection.html

I've solved it by using CopyOnWriteArraySet instead of HashSet.

Thanks for the suggestions anyway, it can help someone else.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top