I wrote a Multithreaded observer pattern in Java
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* An observer pattern that allows listeners to register(), unregister() in
* multiple threads and also notify listeners in another thread.
*
* A HashMap keeps track of the listeners and their status (active, obsolete).
* When a listener unregister, its entry is marked as obsolete in this map.
*
* During firing of an event, the observer notifies all the listeners that are
* active, the active status will be stored in a Boolean that's synchronized so
* rare race conditions like calling notify on an active listener that has just
* turned obsolete will not happen.
*
*
*/
public class MultithreadedObserverPattern {
interface Handler {
void handleEvent(T listener);
}
class BooleanHolder {
boolean val;
BooleanHolder(boolean v) {
val = v;
}
void set(boolean v) {
val = v;
}
boolean get() {
return val;
}
}
Map listeners = new HashMap();
public void register(AbstractListener l) {
synchronized (listeners) {
listeners.put(l, new BooleanHolder(true));
}
}
public void unregister(AbstractListener l) {
synchronized (listeners) {
BooleanHolder status = listeners.get(l);
if (status != null) {
// notify call also syncing on status
synchronized (status) {
status.set(false);
}
}
// set to false
}
}
public void notifyAll(Handler handler) {
// here we do not synchroznie on listeners to avoid tricky lock situations
// make a copy of the map
List> activeListeners = new ArrayList>();
List inactiveListeners = new ArrayList();
synchronized (listeners) {
for (Entry entry : listeners.entrySet()) {
if (entry.getValue().get()) {
activeListeners.add(entry);
} else {
inactiveListeners.add(entry.getKey());
}
}
}
// call the method on active listener
//
for (Entry e : activeListeners) {
BooleanHolder status = e.getValue();
// remove those listeners that are no longer active
synchronized (status) {
if (status.get()) {
handler.handleEvent(e.getKey());
}
}
}
synchronized (listeners) {
// remove inactive listeners
for (AbstractListener l : inactiveListeners) {
listeners.remove(l);
}
}
}
}