Concurrent collection of listeners - java

I am dealing with some problem. I found a solution that works, but I'm still confused and think that there is a better one.
I have model class with collection of listeners of type let's say IListener. Other classes are allowed to add or remove listener via public methods.
My model class uses 2 threads to update some data and after this process is finished, listeners are notified about model changes.
So code goes like this
void updateThread1() {
synchronized (lock) {
updateLogic();
List<IListener> listenersCopy = new ArrayList<>(listeners);
for (IListener listener : listenersCopy) {
listener.dataUpdated();
}
}
void updateThread2() {
synchronized (lock) {
updateLogic2();
List<IListener> listenersCopy = new ArrayList<>(listeners);
for (IListener listener : listenersCopy) {
listener.dataUpdated();
}
}
And here is my problem: as listeners can be added or removed by different threads then updateThread1 and updateThread2 so there is a pretty good chance to get ConcurrentModificationException.
I can't tell whether creating copy of array is atomic (i.e. ArrayList#addAll() uses some native array copy mechanizm).
I tried to used CopyOnWriteArrayList, which works fine. Second approach was to use concurrent hash set (from Collections#newSetFromMap()) that was backed by ConcurrentHashMap.
This works for me, but I'd like to know whether there are some design approaches to make it work.
I'd appreciate any help. Thanks.

Related

Should listeners be able to remove listeners?

Many classes use code similar to the following to fire listeners.
private List<Listener> listeners = new ArrayList<Listener>();
public void fireListener() {
for(Listener l : listeners) l.someMethod();
}
This works fine until the listener tries to add/remove a listener. This modification from inside the list causes a ConcurrentModificationException. Should we handle this case or should modifying listeners be invalid? What would be the best way to handle add/remove of listeners?
Update:
Here is one possible solution.
public void fireListener() {
for(Listener l : listeners.toArray(new Listener[listeners.size()])) {
if(!listeners.contains(l)) continue;
l.someMethod();
}
}
There are three cases:
You don't want to allow the modification of the listeners collection during listeners execution:
A ConcurrentModificationException would be appropriate in this case.
You want to allow modification of listeners, but the changes are not to be reflected in the current run:
You have to make sure a modification of listeners does not have impact on the current run. A CopyOnWriteArrayList does the trick. Before using it read the API, there are some pitfalls.
Another solution would be copying the list before iterating through it.
You want changes to listeners reflect within the current run:
The most tricky case. Using for-each loops and iterators won't work here (as you have noticed ;-)). You have to keep track of changes yourself.
A possbile solution would be to store listeners in an ArrayList and go through that list using a standard for loop:
for (int i =0; i < listeners.size(); i++) {
Listener l = listeners.get(i);
if (l == null)
continue;
l.handleEvent();
}
Removing listeners would set the element at its place in the array to null.
New listeners are added to the end and therefore will run in the current execution.
Notice that this solution is just an example and not threadsafe!
Maybe some maintainance is necessary to remove null elements sometimes to keep the list from growing too big.
It is up to you to decide what is needed.
My personal favourite would be the second one. It allows modifications while execution but does not change the current run's behaviour which can cause unexpected results.
The right way to handle the addition/removal of listeners while browsing them would be to use an iterator that supports this.
For example:
// Assuming the following:
final List<Listener> listeners = ...
final Iterator<Listener> i = listeners.iterator();
while (i.hasNext()) {
// Must be called before you can call i.remove()
final Listener listener = i.next();
// Fire an event to the listener
i.notify();
if (/* true iff listener wants to be deregistered */) {
i.remove(); // that's where the magic happens :)
}
}
NB: Please note that certain Iterators do not support the Iterator#remove method!
I would say it isn't a very good idea to allow listeners to add/remove other listeners (or themselves). It indicates a poor separation of concerns. After all, why should the listener know anything about the caller? How would you test such a tightly coupled system?
Something you can do instead is have your event handling method (in the listener) return a boolean flag that indicates that the listener doesn't want to receive more events. That makes it the responsibility of the event dispatcher to do the removal, and it covers most use cases where you need to modify the list of listeners from within a listener.
The major difference in this approach is that the listener simply says something about itself (i.e. "I don't want more events") rather than being tied to the implementation of the dispatcher. This decoupling improves testability and doesn't expose the internals of either class to the other.
public interface FooListener {
/**
* #return False if listener doesn't want to receive further events.
*/
public boolean handleEvent(FooEvent event);
}
public class Dispatcher {
private final List<FooListener> listeners;
public void dispatch(FooEvent event) {
Iterator<FooListener> it = listeners.iterator();
while (it.hasNext()) {
if (!it.next().handleEvent(event))
it.remove();
}
}
}
Update: Adding and removing other listeners from within a listener is slightly more problematic (and should set off even louder alarm bells) but you can follow a similar pattern: the listener should return information on what other listeners need to be added/removed and the dispatcher should act on that information.
However in this scenario you get quite a few edge cases:
What should happen with the current event?
Should it be dispatched to the newly added listeners?
Should it be dispatched to the ones about to be removed?
What if you're trying to remove something that is earlier in the list and the event has already been sent to it?
Also, what if listener X adds listener Y and then listener X is removed? Should Y go with it?
All these problems come from the listener pattern itself and the basic assumption of it that all the listeners in the list will be independent of each other. And the logic to handle these cases should definitely go in the dispatcher and not in the listener.
Update 2: In my example I used a naked boolean for brevity but in real code I'd define a two-valued enum type to make the contract more explicit.
You may first make a copy of listeners Collection to avoid possible ConcurrentModificationException:
public void fireListener() {
Listener[] ll = listeners.toArray(new Listener[listeners.size()]);
for(Listener l : ll) l.someMethod();
}
You can't add/remove to a list/map that's being used. In C# there's a nice little class called ConcurrentList/ConcurrentDictionary.
In Java this is possible, here a nice little link for you:
https://docs.oracle.com/javase/tutorial/essential/concurrency/collections.html
Since a collection is used within a loop, the collection is used for a certain time. If you add/remove in the function called within the loop, you'll get an error as you're not allowed to without Concurrent.

Jira: Thread-safe Gadget Data?

I have some data (two HashSets and a timestamp Instant) that I'd like all requests to my JIRA (OpenSocial?) gadget/plugin to share -- because it takes a long time to generate (couple of minutes) and because the sharing will help the requests be more performant.
Occasionally (very rarely), a request might include a parameter that indicates this shared data should be refreshed. And of course the first time it's needed, it gets populated. It is okay for the data to represent a stale answer -- it is based on things that change slowly and used to visualize trends so off-by-one errors are tolerable.
I imagine when JIRA starts up (or I upload a new version of my add-on) and multiple requests come in during the first couple of minutes, I'd need to handle the population of this expensive shared data in a thread-safe way. Currently the results look fine but as I understand it, that's been just due to chance.
Only one thread needs to do the work of populating. On start-up, the other threads will have to wait of course because they can't skip ahead empty-handed. (If all threads do the expensive initialization, that's a lot of unnecessary load on the server)
But after the initial cost, if multiple concurrent requests come in and one of them includes the 'refresh' parameter, only that one thread needs to pay the price -- I'm fine with the other threads using an old copy of the expensive data and thereby staying performant, and including in the response that "yes someone out there is refreshing the data but here's a result using an old copy".
More about the data: The two HashSets and the timestamp are intended to represent a consistent snapshot in time. The HashSet contents depend on values in the database only, and the timestamp is just the time of the most recent refresh. None of this data depends on any earlier snapshot in time. And none of it depends on program state either. The timestamp is only used to answer the question "how old is this data" in a rough sense. Every time the data is refreshed, I'd expect the timestamp to be more recent but nothing is going to break if it's wrong. It's just for debugging and transparency. Since a snapshot doesn't depend on earlier snapshots or the program state, it could be wrapped and marked as volatile.
Is there an obvious choice for the best way to go about this? Pros and cons of alternatives?
You'll want to use Locks to synchronize access to the sections of your code that you need to have only one thread executing at once. There are plenty of resources on SO and in the Oracle Java docs that show how to use locks in more detail, but something like this should do the trick.
The idea is that you want to maintain a copy of the most-recently generated set of results, and you always return that copy until you have a new set of data available.
import java.util.concurrent.locks.ReentrantLock;
public class MyClass
{
private volatile MyObject completedResults;
private final ReentrantLock resultsLock;
private final ReentrantLock refreshLock;
public MyClass()
{
// This must be a singleton class (such as a servlet) for this to work, since every
// thread needs to be accessing the same lock.
resultsLock = new ReentrantLock();
refreshLock = new ReentrantLock();
}
public MyObject myMethodToRequestResults(boolean refresh)
{
MyObject resultsToReturn;
// Serialize access to get the most-recently completed set of results; if none exists,
// we need to generate it and all requesting threads need to wait.
resultsLock.lock();
try
{
if (completedResults == null)
{
completedResults = generateResults();
refresh = false; // we just generated it, so no point in redoing it below
}
resultsToReturn = completedResults;
}
finally
{
resultsLock.unlock();
}
if (refresh)
{
// If someone else is regenerating, we just return the old data and tell the caller that.
if (!refreshLock.tryLock())
{
// create a copy of the results to return, since we're about to modify it on the next line
// and we don't want to change the (shared) original!
resultsToReturn = new MyObject(resultsToReturn);
resultsToReturn.setSomeoneElseIsRegeneratingTheStuffRightNow(true);
}
else
{
try
{
completedResults = generateResults();
resultsToReturn = completedResults;
}
finally
{
refreshLock.unlock();
}
}
}
return resultsToReturn;
}
}

Synchronized Not Entering

Note: I'm not looking for workarounds; I'm sure I can find other methods if necessary. I simply feel like I'm missing something fundamental or quirky and I want to know what I'm missing. Or if there is a way to use the debugger to get more info that would be nice too. Thanks!
I'm having an issue with use of synchronized. I'm receiving deadlock but it seems utterly impossible. I've placed print statements before each and every synchronized call, just inside each call, and just before exiting so I can see who all holds which synchronized objects. I'm finding that it will not go inside one of my synchronized calls even though no one currently holds the lock on the object. Are there some kind of quirks that I'm missing or illegal nesting operations? Here's the jist of what I am doing.
Oh yeah, and the oddest thing is that removing the two "busyFlagObject" synchronizations makes it work fine...
Thread 1:
public void DrawFunction()
{
synchronized(drawObject)
{
...
// Hangs here though nobody has a lock on this object
synchronized(animationObject)
{
}
}
}
Thread 2:
public void AnotherFunction()
{
synchronized(busyFlagObject)
{
// Calls a function that also uses this same Synchronized call
synchronized(busyFlagObject)
{
// Calls another function that uses another Synchronized call
// Hangs here waiting for the draw function to complete which it SHOULD
// be able to do no problem.
synchronized(drawObject)
{
}
// Never gets to this one assuming the Log statements don't
// buffer and aren't flushed but still shouldn't be a problem anyway.
synchronized(animationObject)
{
}
}
}
}
Run your app under the debugger or use "jstack" from the JDK tools. That will show you directly which threads wait for locks and which hold locks, so we don't have to guess where your problem is :-)
That said, you mention you synchronize on Boolean. Keep in mind that the class is intended to only have two instances, and many things (particularly boxing) will implicitly change your Boolean instance to the "shared" value. Are you sure your lock objects are not the same instance? You might consider using new Object() as your monitor object.
It's worth noting that this isn't the only place that this can happen and there's a good entry on this problem in Java Concurrency in Practice, specifically with string interning, that I'm failing to find a link to at the moment. Don't use a type that isn't under your control as something it wasn't intended to do :-)

Is it a problem that my listener implementation is making a new array every time the listener is fired?

I have an object in my design that has listeners. These listeners get fired on a certain event that can occur as many as a hundred times per second.
I was doing something like this:
private void notifyListeners(ObjectEvent o) {
synchronized (this.listeners) {
for (ObjectListener l: this.listeners)
l.eventFired(o);
}
}
The problem here is that someone can implement an eventFired method which then turns around and waits for a synchronizeto an object that's being held by a different thread which is trying to add or remove a listener and waiting on a synchronized(this.listeners) line.
So, I modified the notifyListeners method thusly:
private ObjectListener[] getObjectListeners() {
synchronized (this.listeners) {
return this.listeners.toArray(new ObjectListener[this.listeners.size()]);
}
}
private void notifyListeners(ObjectEvent o) {
ObjectListener[] listeners = this.getObjectListeners();
for (ObjectListener l: listeners)
l.eventFired(o);
}
I'm worried about the impact of creating this array every time the object is fired, and the impact this will have on the memory usage of the application.
So I'm here to ask if there's a better way. I may have just found it, though. It would be easy enough to create the ObjectListener[] every time I add or remove a listener and then just iterate through that with no synchronization every time the event fires. I'm going to make that change now, and then see if there's a better idea offered up here.
Use CopyOnWriteArrayList instead. The observation is that listener lists are typically inspected a lot more often than they are modified.

How can one overcome a concurrent modification exception in a GUI mediator?

FYI I have adopted the mediator pattern for my GUI in Swing on Java.
Unfortunately, a concurrent modification exception is always thrown if user input requires a new window.
It's because my code attempts to add a new colleague (a new window) to the mediator's list of colleagues in the course of handling user input from an existing colleague (window).
e.g.
public MainScreenColleague implements GuiColleague, ActionListener {
private GuiMediator mediator;
public MainScreenColleague(GuiMediator medi) {
mediator = medi;
// implement JFrame with JButtons
}
public conveyInputToMediator(EventObject event) {
mediator.conveyInputToColleagues(event);
}
public receiveInputFromMediator(EventObject event) {
if (event.getSource() = particularBtn) {
GuiColleague particularColleague = new ParticularConcreteColleague(mediator);
//THIS IS THE CODE THAT THROWS CONCURRENCY EXCEPTION
mediator.addGuiColleague(particularColleague);
}
}
Is there some other structure of handling adding new colleagues that I can adopt? Thanks in advance for any suggestions or ideas.
One option might be to adopt a Swing-esque model and have your mediator store an "event queue" of updates that it needs to make when the time is right. That way, when you're adding new windows while processing events from some other object, that window doesn't gum up the logic. It just gets added to a "handle this when you're done" queue that gets processed after the mediation is done.
Another option would be to make a copy of the list before iterating over it, so that changes made to the raw structure during iteration don't show up in the list being iterated over.
Yet another idea would be to use some non-iterator technique for visiting elements. For example, if you had the windows stored in a list, you could use a for loop like this one:
for (int i = 0; i < elems.size(); ++i)
/* ... */
which will not throw these sorts of exceptions.
If the traversals far outnumber the modifications, a copy-on-write datastructure like CopyOnWriteArrayList may fit.

Categories

Resources