I read this puzzle years ago (unfortunately I can’t find the reference) but didn’t appreciate it at the time. Java synchronized methods [1] can supposedly be invoked only by a single thread at a time. The essence of aforementioned puzzle was that, if the synchronized method obtains a lock on the object the method belongs to, then the method can be invoked from a different thread. The test below demonstrates this effect:
public class TestSynchronizedMethod { static class Worker implements Runnable{ @Override public synchronized void run() { String name = Thread.currentThread().getName(); System.out.println(name+" started run"); synchronized(this){ try { System.out.println(name+" waiting for mutex"); this.wait(); System.out.println(name+" done waiting for mutex"); } catch (InterruptedException e) { System.out.println(name+" interrupted"); e.printStackTrace(); } } } } public static void main(String...args) throws Exception{ Worker worker = new Worker(); Thread t1 = new Thread(worker, "Thread 1"); Thread t2 = new Thread(worker, "Thread 2"); System.out.println(t1.getName()+" starting"); t1.start(); System.out.println(t1.getName()+" started"); System.out.println(t2.getName()+" starting"); t2.start(); System.out.println(t2.getName()+" started"); t1.join(); t2.join(); } }
The test produces an output similar to this:
Thread 1 starting
Thread 1 started
Thread 2 starting
Thread 1 started run
Thread 1 waiting for mutex
Thread 2 started
Thread 2 started run
Thread 2 waiting for mutex
If synchronized methods can’t be invoked by two threads at the same time, then the last two lines where the second thread enters the worker’s run method while it’s locked by the first thread shouldn’t be printed.
Be smart, don’t synchronize methods.
Resources
[1] Java synchronized methods
https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html
Sorry to tell you but you are wrong. That two last lines are printed because thread 1 call on wait() releases worker monitor.
Also the synchronized(this) is superflous. See:
https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html
LikeLike
The “documentation” [1] is clear: “it is not possible for two invocations of synchronized methods on the same object to interleave”. The example has a single Worker instance with two threads trying to invoke the same synchronized method at the same time – this shouldn't be possible according to [1]. The wait() call clears any locks on the synchronized method (or disables whatever mechanism the JVM uses) and allows a different thread to enter the method while it is still executed by the first thread.
As a simple experiment you can replace wait() with something else blocking, e.g. Thread.sleep(2000); you'll notice that the line “Thread 2 started run” isn't printed until 2 seconds have passed.
[1] https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html
LikeLike
I see your point, it is just a matter of interpretation. The same paragraph you mention ends with “until the first thread is done with the object.” Calling wait() you declare explicitly you are done with the object.
It is common knowledge that a synchronized method is shorthand to synchronized(this) full body *and* make it public API.
LikeLike
> It is common knowledge that a synchronized method is shorthand to synchronized(this) full body *and* make it public API.
Do you have a reference? I couldn't find anything about how the JVM implements method synchronization.
LikeLike