Time to dream the solution to the dreams puzzle. As a reminder, here’s the challenge:
package sleep;
import dream.Dream;
public class Sleeper {
private int level;
public synchronized int enter(Dream dream) {
level++;
try {
dream.dream(this);
} finally {
level--;
}
return level;
}
}
package sleep;
import dream.Dream;
public class Main {
public static void main(String[] args) {
if (new Sleeper().enter(new Dream()) != 0) {
// The goal is to reach this line
System.out.println("Am I still dreaming?");
}
}
}
Solution
The synchronized on the dream method means that it takes the monitor (lock) of the Sleeper object when we enter it, and it releases it again when we leave it. No other thread can enter the method while the monitor is held.
But here’s the catch: the monitor isn’t necessarily held all the time while the method is running. With Object.wait you can release a monitor in the middle of a synchronized method.
How do we apply that to solve this puzzle? When we enter the outer dream, we use wait to release the monitor. But first we launch a separate thread, that will also enter a dream. Entering that inner dream makes the second thread take the monitor. So in the inner dream, we use wait a second time to release the monitor. That allows the main thread stop waiting, take the monitor back, and leave the outer dream. And there we are: we have woken up from the outer dream, back where we started in main, but the dream within the dream goes on in the second thread.
package dream;
import sleep.Sleeper;
public class Dream {
private static void doWait(Sleeper s) {
try {
s.wait(200);
} catch (InterruptedException e) {
}
}
public void dream(final Sleeper s) {
new Thread() {
public void run() {
s.enter(new Dream() {
@Override
public void dream(Sleeper s) {
doWait(s);
}
});
}
}.start();
doWait(s);
}
}

And the winner is…
I got more answers than I expected. The first one came from David Shay. Congrats!
It’s a bit repetitive, but as promised, you can see and compare all dreams as comments below this post.
Conclusion
The moral of the story: synchronized on a method doesn’t mean two different threads can’t be in it at the same time. It only ensures that such threads cannot execute concurrently; at least one of them must be waiting.
You can prevent this kind of abuse and other problems, by using a private object as monitor:
public class Sleeper {
private final Object lock = new Object();
public int dream(Dream dream) {
synchronized(lock) {
...
}
}
}
But that still leaves me unsure if I actually woke up this morning.


