Sorting out listener implementations in Java
Recently I scanned the source code of our current Eclipse RCP/RAP project for occurrences of the Listener (or Observer) pattern. I found no less than 6 (!) different implementations and decided to unify them into one. When researching the implementation options (and the Java world has many to offer), I found an aspect of Listener programming that is usually not mentioned by internet resources. But first, let’s look at the options.
In general, the solutions for thread safety can be divided into three classes: heavyweight, lightweight or no synchronization.
No synchronization
private final List listeners = new ArrayList(); public void addListener(Listener listener) { listeners.add(listener); } public void removeListener(Listener listener) { listeners.remove(listener); } private void notifyListeners() { for (Listener listener : listeners) { listener.doSomething(); } } |
This solution works fine in a single-threaded environment but doesn’t work well in a multi-threaded application. When one thread is adding or removing a listener while another thread is looping over the listeners, you will get the infamous java.lang.ConcurrentModificationException. So, some synchronization is needed.
Heavyweight synchronization
Here, the number of threads accessing the list is limited by locks. The most common example is the use of Java’s synchronized keyword.
private final List listeners = new ArrayList(); public void addListener(Listener listener) { synchronized (listeners) { listeners.add(listener); } } public void removeListener(Listener listener) { synchronized (listeners) { listeners.remove(listener); } } private void notifyListeners() { synchronized (listeners) { for (Listener listener : listeners) { listener.doSomething(); } } } |
This approach works well but is not generally regarded as the best solution. While one thread has the lock, the others have to wait, which can slow down your application considerably. Additionally there is always risk of deadlocks when synchronized comes into play.
Lightweight synchronization
With Lightweight synchronization, the idea is to loop over a copy of the list or to use a list which allows concurrent modification. This makes explicit locking unnecessary. There are many implementations for this pattern in the Java world. Just to name a few:
- Replace the
ArrayListwith java.util.concurrent.CopyOnWriteArrayList - Replace the
ArrayListwith java.util.concurrent.ConcurrentLinkedQueue - In Swing use javax.swing.event.EventListenerList
- In Eclipse RCP/RAP use org.eclipse.core.runtime.ListenerList
Each of these approaches have their pros and cons which are usually described well in their JavaDoc or the numerous articles and blog posts about the topic. What’s not usually mentioned is one characteristic all of these solutions have in common: if a listener is removed while another thread notifies the listeners, there is no guarantee that the removal of the listener is reflected in the ongoing notification.
This means in practice that methods on a listener could be called after it has been removed! Imagine the case that one thread removes the listener from the list and releases all resources the listener is holding (which seems ok since they are not needed anymore). Then another thread finds the listener in the (outdated) listener list and executes a method which requires the resources which have already been released.
Now, what is our conclusion here? Either you use heavyweight synchronization or, when using lightweight synchronization, you have to make sure that:
- the
notifyListenersshould be programmed in a way that it can handle exceptions coming from the listeners, i.e. a try-catch around the listener method call - all public methods in the listener should gracefully handle the case that they are called although the listener is already marked for disposal
The first point is a best practice for listener implementations anyway. The second is probably not an problem for most listeners but it’s important to keep it in mind when programming – bugs related to multi-threading are usually Heisenbugs and hard to fix.





As you mentioned heavy weight synchronization is _very_ prone to deadlocks, so much so that I certainly would not use it. In fact multi-threaded event based systems almost always have bugs in my experience and are difficult to debug.
I think the best way to solve the problem is to have thread confined observables. When you have an event that you want to post, put it into a queue which the observable will then fire. I have found this approach leads to much simpler code and is more robust.
Hi Andy,
If I understand your approach correctly, you use a dedicated notifier thread whose only task is to notify the observers.
I like the idea to decouple the creation of the event from the notification of the listeners, this should decrease the risk of deadlocks significantly. There is one side effect though: listener notification is always asynchronous, i.e. the emitter of the event does not know when the listeners are notified. In general this should not be a problem, but you have to keep it in mind when programming.
Your notifier thread must also manage a list of observables. You’re saying that the observables are thread confined – how do you add and remove observables then?
An ArrayList is not suitable when adding or removing several items one by one, which is generally the case when initializing or disposing the graphical representation of a dataset, or any similar use involving a large amount of listeners on the same source.
A LinkedList is more suitable for common cases, but for optimal performance using a SynchronizedSet is better when dealing with more than 1 000 listeners on the same source : it ensures a listener wll be removed quickly and will not be added twice, even if iterating through a copy of the set is still required to avoid ConcurrentModificationException.