Nested Locks

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 .)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset