1
votes

My understanding of Java synchronized() blocks is that, if a thread already owns a lock on an object, it can enter a different block synchronized on the same object (re-entrant synchronization). Underneath, I believe that the JVM uses a reference count to increment/decrement the number of times a thread has acquired a lock, and that the lock is only released when the count is zero.

So my question is, if one encounters a piece of code that looks like this:

synchronized(this)
{
    if (condition == WAITING)
    {
        synchronized(this)
        {
            condition = COMPLETE;

            notify();

            try
            {
                wait();
            }
            catch(InterruptedException  e)
            {
            }
        }
    }
    else
        condition = READY;
}

what specifically happens when wait() is called? Does it merely decrement the count, or does it release the lock regardless of the count?

In the first case, it seems to me that it will produce a deadlock if lock re-entry had occurred, because it will still own the lock and would thus wait forever on another thread which is waiting on it.

In the second case, I can't really see what the point of the second synchronized block is at all.

The documentation for wait() says

"The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution,"

so I think that the second case is the correct one, but I could be wrong. So am I missing something, or have I merely come across a redundant synchronized block that could just as easily be removed from the code?

1
If you see code like this (some of which doesn't compile) I suggest you delete and re-write it. Note: the first time you call notify, nothing will happen as no thread will be waiting. The second time, you won't call notify as the condition has changed, so your first thread will wait for ever. - Peter Lawrey
I (poorly) edited the code to simplify it as an example. The condition stuff and the uncompilable errors were me, not the code/issue itself. Also, condition is updated elsewhere, by another thread. The basic jist of the code is for a thread waiting on another thread to complete to be notified that the condition has changed. It, then, calls notify elsewhere when it has finished. - user2725525
Why don't you rewrite this to use a Futures so that you know when a thread has completed? - M.K.
a) Because I'm porting this to C++, not maintaining the current Java code, and b) Even so, 'complete' was a misnomer on my part. wait and notify are really dependent on internal state here, not any actual computational output. - user2725525
Futures are not just for computations. Also C++ has futures and promise. - M.K.

1 Answers

6
votes

There's nothing that necessitates the reacquiring of the lock after the if.

The wait() will also release the lock completely (otherwise it would be quite deadlock prone).

The only reason for the second synchronized that I can see, is that it previously used another object and someone modified it to use the same this by mistake.