Should I synchronize listener notifications, or not?

你说的曾经没有我的故事 提交于 2019-12-03 20:20:33

Use a CopyOnWriteArrayList for your listener arrays.

This is perfect for listener arrays which change infrequently. When you iterate through them, you're iterating through the underlying array. With a CopyOnWriteArrayList, this array is copied every time it is modified. So there is no need to synchronize with it when iterating because each underlying array is guaranteed to be static, even past its use within the CopyOnWriteArrayList.

Since CopyOnWriteArrayList is also thread safe, you do not need to synchronize the add & remove operations.

Declaration:

private final CopyOnWriteArrayList<Listener> listeners;

Event trigger:

for (Listener l: this.listeners) {
  l.event();
}

I would use a ConcurrentLinkedQueue<Listener> which is made for this kind of problems: adding, removing and iterating simultaneously on a collection.

A precision : this solution prevents a listener from being called from the very moment it is deregistered. This solution has the best accuracy, the finest granularity, and is probably the solution that is the less deadlock-prone.

If you're adamant about your two proposals, I would choose the first one because it is more secure, but it's likely to provoke longer locks and reduce overall performance (well it depends on how often you add or remove listeners). The second solution is broken because when a listener deregisters itself, it might well be because he's become unable to handle events. In such a case, calling it would be a very bad idea, and in any case it would be a violation of the listener contract.

I guess I would choose the second option too. Like I think you said, the second option doesn't hold the lock when it's notifying the listeners. So, if one of the listeners takes a long time to do its thing, other threads can still call the addListener or removeListener methods without having to wait for the lock to be released.

there are several datastructures available that allow concurrent adding, removing and iterating

a ConcurrentLinkedQueue is fully lockless and fast on add and remove (bar the O(n) traversel to find it) and add, but there may be some interference of other threads (a bulk remove may only be partially visible to the iterator)

the copyOnWrite list and set are slower on add and remove because they entail a array allocation and copy, however the iteration is completely free of interference and as fast as traversing a ArrayList of the same size (the iteration happens on a snapshot of the set)

you can build a ConcurrentHashSet on the ConcurrentHashMap but this has the same (usefull)properties besides a fast O(1) remove and the locks used for adding and removing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!