Let’s
examine our BusyFlag class yet again. Suppose we add another method
that finds out which thread owns the lock. This
getBusyFlagOwner()
method simply returns the
busyflag
, which just so happens to be the thread
object that owns the lock. An implementation is as follows:
public synchronized Thread getBusyFlagOwner() { return busyflag; }
Furthermore, let’s make a modification to the
freeBusyFlag()
method to use this new
getBusyFlagOwner()
method:
public synchronized void freeBusyFlag () { if (getBusyFlagOwner() == Thread.currentThread()) { busyflag = null; } }
In this version of the freeBusyFlag()
method, we
make a call to the getBusyFlagOwner()
method to
see if the current thread is the owner before freeing the
busyflag
. What is interesting here is that both
the freeBusyFlag()
and the
getBusyFlagOwner()
methods are synchronized. So
what happens? Does the thread hang at the
getBusyFlagOwner()
method while waiting for the
freeBusyFlag()
method to free the object lock?
If not, and the getBusyFlagOwner()
method is
allowed to run, what happens when that method completes? Does it free
the object lock even though the freeBusyFlag()
method still needs it? The answer to all these questions is that it
all works the way you want it to.
A synchronized area (by which we mean a synchronized block or method)
does not blindly grab the lock when it enters the code section and
free the lock when it exits. If the current thread already owns the
object lock, there is no reason to wait for the lock to be freed or
even to grab the lock. Instead the code in the synchronized area
merely executes. Furthermore, the system is smart enough not to free
the lock if it did not initially grab it upon entering the
synchronized area. This means that the
freeBusyFlag()
method can call the
getBusyFlagOwner()
method without any problems.
Unfortunately, our version of the locking mechanism, the BusyFlag
class, is not so smart. It hangs waiting for the lock that it is
currently holding to be freed. To solve this problem, we must
reimplement the BusyFlag class with a counter. The object now checks
to see if it already owns the lock and increases the count by one if
it does. In the corresponding freeBusyFlag()
method, it only frees the busyflag
if the count
is zero. This way a thread within the scope of a BusyFlag lock
directly or indirectly (through method calls) enters other areas that
are locked with the same BusyFlag instance.
Here’s an implementation (still suboptimal) of the BusyFlag class with this modification:
public class BusyFlag { protected Thread busyflag = null; protected int busycount = 0; public void getBusyFlag() { while (tryGetBusyFlag() == false) { try { Thread.sleep(100); } catch (Exception e) {} } } public synchronized boolean tryGetBusyFlag() { if (busyflag == null) { busyflag = Thread.currentThread(); busycount = 1; return true; } if (busyflag == Thread.currentThread()) { busycount++; return true; } return false; } public synchronized void freeBusyFlag () { if (getBusyFlagOwner() == Thread.currentThread()) { busycount—; if (busycount == 0) busyflag = null; } } public synchronized Thread getBusyFlagOwner() { return busyflag; } }
With this new implementation of the BusyFlag class, we can now lock any section of code without worrying that we may already own the lock. We can also free the lock without worrying. Both the synchronized mechanism and our BusyFlag class can be used as nested locks. (The BusyFlag class is now beginning to resemble another synchronization primitive known as a semaphore .)