Last updated
on 22-12-2009
at 12:00 PM

Blog

In my 3rd year project (J2ME) there’s a lot of threads and locking. I wasn’t sure exactly how some of that works, in either real Java or J2ME, so I’ve run some tests.

Specifically I wanted to know exactly what happens if you write code like:

... synchronized (lock) { ... lock = new Lock(); ... } ...

Locks are supposed to be on an object, not a reference, which means once you change lock to a different object you are now holding the monitor of an object you don’t necessarily have a reference to, which is a bit weird.

Even more questions start coming out of that when you start wondering what other threads do with this. To start with, what happens if you have a lock, you change the variable you used, and then somebody comes to another block on that variable, after the object’s been changed? We can test this with:

private static Object lock = new Object(); public static void main(String[] args) { System.out.println("starting a"); new Thread(new ThreadA()).start(); System.out.println("starting b"); new Thread(new ThreadB()).start(); } private static class ThreadA implements Runnable { public void run() { try { System.out.println("a started"); synchronized(lock) { System.out.println("a has lock"); Thread.sleep(2000); System.out.println("a resets lock"); lock = new Object(); Thread.sleep(2000); System.out.println("a done"); } } catch (Exception e) { System.out.println("Interrupted"); } } } private static class ThreadB implements Runnable { public void run() { try { System.out.println("b started"); Thread.sleep(3000); System.out.println("b looking for lock"); synchronized(lock) { System.out.println("b gets lock"); } } catch (Exception e) { System.out.println("Interrupted"); } } }

The various Thread.sleep()s in here are should organise this into a nice sequential system, that you can look at and see when things hit what, and what is sitting on which locks. It’s worth noting that the javadoc for thread.sleep specifically says it doesn’t release any monitors it might have. The above produces:

starting a starting b b started a started a has lock a resets lock b looking for lock b gets lock a done

I think that shows fairly solidly that if you change a lock reference, whilst synchronized on the original lock, and somebody then subsequently attempts to get a hold of the lock for that reference, they get it. This isn’t that surprising. The problem comes when you change the order of things, by changing the length of the first sleep in Thread B to less than that ofthe first sleep in Thread A, so that Thread B hits the synchronized block before A changes the reference. If you do this then B waits on the monitor for the object that is referred to by the that reference, and when you subsequently change the reference, B waits on the original monitor, not the object the reference now points to. To be (very) specific, we can demonstrate this:

private static Object lock = new Object(); public static void main(String[] args) { System.out.println("starting a"); new Thread(new ThreadA()).start(); System.out.println("starting b"); new Thread(new ThreadB()).start(); } private static class ThreadA implements Runnable { public void run() { try { System.out.println("a started"); synchronized(lock) { System.out.println("a has lock"); Thread.sleep(2000); System.out.println("a resets lock"); lock = new Object(); Thread.sleep(2000); System.out.println("a done"); } } catch (Exception e) { System.out.println("Interrupted"); } } } private static class ThreadB implements Runnable { public void run() { try { System.out.println("b started"); Thread.sleep(1000); System.out.println("b looking for lock"); synchronized(lock) { System.out.println("b gets lock"); } } catch (Exception e) { System.out.println("Interrupted"); } } }

(The only difference here is that the Thread.sleep in Thread B is now 1000, not 3000)

This results in the following output:

starting a starting b b started a started a has lock b looking for lock a resets lock a done b gets lock

That includes a great big pause after ‘a resets lock’ where A is locked on the first lock object, the lock reference points to a new lock object (which had a free monitor), B is at the synchronized(lock) line, and B does not move into the block. As soon as A leaves the block, B gets the lock (on the old object) and continues.

This is a bit nasty, as if b now reads from the lock variable it will be reading from the new object, not the old one that it’s actually holding the monitor for, and nobody’ll suspect a thing. It is at least consistent though, and now at least a little more specifically documented! It seems to work exactly the same way in both J2SE (1.6) and J2ME and makes sense in the context of a wait/notify style action, where hitting a synchronized() line calls a wait() on the relevant object’s monitor, and leaving a block calls a notify. Hope this helps somebody!