IllegalMonitorStateException - Lock lost, but why?

I have some kind of synchronization problem. somehow I get an IllegalMonitorStateException when calling notify on an object, but this object is within a synchronized block! The code looks like this:

privatevolatile Set<K> set =new HashSet<K>();

private Map<K, ArrayList><Listener>> map =new HashMap<K, ArrayList><Listener>>();

publicvoid findClass(K key, Listener listener){

synchronized (set){

synchronized (map){

set.add(key);

ArrayList<Listener> listeningClasses = map.get(key);

if (listeningClasses ==null){

listeningClasses =new ArrayList<Listener>(2);

map.put(key, listeningClasses);

}

listeningClasses.add(listener);

}

set.notify();

}

}

publicvoid run(){

while (!stopped){

synchronized (set){

while (set.size() == 0){

try{

set.wait();

}catch (InterruptedException e){

}

}

}

// create a new map and exchange it with the publicly accessible map

Set<K> curSet =null;

Set<K> newSet =new HashSet<K>();

synchronized (set){

curSet = set;

set = newSet;

}

}

As far as I know about synchronization this should work. A single thread waits within its run method. This wait is within the synchronized block. When findClass is called, an entry is added to the set and finally notify is called to wake up the thread.

The line that calls notify triggers the IllegalMonitorStateException. As the object referenced by set doesn't change, as changing of the referenced object also happens within a synchronized block.

The exception only occurs quite rare, but is a big problem for the application I am working on. Has anyone an idea?

Greetz, max

[3166 byte] By [maxpga] at [2007-11-27 9:07:00]
# 1

Problem:

Imagine following situation.

First thread is inside the (on frist line of the synchronized block:

synchronized (set) {

curSet = set;

set = newSet;

}

Second is trying to enter the synchronized block with notify. And is blocked waiting on lock on "set" variable, which is instanceA. In the first thread you change the set variable to instanceB and leave the synchronized block.

Second thread has lock on instanceA, and tries to execute notify() on instanceB (new "set "variable value).

Solution:

Do not synchronize on the "set" variable. Instead create a new variable for this specific cause, and synchronized on it, for example:

private Object myLock=new Object;

Post scriptum:

Never synchronize on variable that might change it's referencing object.

data-lossa at 2007-7-12 21:43:09 > top of Java-index,Core,Core APIs...
# 2
Of course, thats it! Thank you very much. I thought that this situation wouldn't be a problem because I didn't think about that the thread that calls notify will wait for the "old" object. But after reading your post it is very clear to me! Again, thanks.
maxpga at 2007-7-12 21:43:09 > top of Java-index,Core,Core APIs...