The Synchronized Block

Notice that the original getBusyFlag() method is not declared as synchronized. This is because it’s not necessary: getBusyFlag() does not try to access the busyflag variable. Instead, it calls the tryGetBusyFlag() method, which accesses the busyflag and is, of course, synchronized. Let’s take another look at the getBusyFlag() method, one that does not call the tryGetBusyFlag() method. Instead, this version gets the busyflag directly:

public synchronized void getBusyFlag() {
     while (true) {
          if (busyflag == null) {
               busyflag = Thread.currentThread();
               break;
          }
          try {
               Thread.sleep(100);
          } catch (Exception e) {}
     }
}

Let’s assume that we do not want the inefficiency of an extra method call to the tryGetBusyFlag() method. In our new version of the getBusyFlag() method, we now access the busyflag directly. The getBusyFlag() method simply loops waiting for the flag to be freed, sets the flag, and returns. Since we are now accessing the busyflag directly, we must make the method synchronized or we will have a race condition.

Unfortunately, there is a problem when we declare this method to be synchronized. While declaring the method synchronized prevents other getBusyFlag() and tryGetBusyFlag() methods from being run at the same time (which prevents a race condition), it also prevents the freeBusyFlag() method from running. This means that if the flag is busy when getBusyFlag() is called, getBusyFlag() waits until the flag is freed. Unfortunately, since the freeBusyFlag() method will not run until the getBusyFlag() method frees the object lock, the busyflag will not be freed. This Catch-22 situation is termed deadlock. The deadlock in this case is a problem between a lock and a busyflag. More commonly, deadlock occurs between two or more locks, but the idea is the same.

We have a problem in this implementation of getBusyFlag() because the scope in which we used the object lock was too large. All we need to do is hold the lock for the period during which we need to change the data (i.e., check and get the busyflag); it doesn’t need to be held during the entire method. Fortunately, Java also provides us the ability to synchronize a block of code instead of synchronizing the entire method. Using this block synchronization mechanism on our getBusyFlag() method, we now obtain the following code:

public void getBusyFlag () {
    while (true) {
        synchronized (this) {
            if (busyflag == null) {
                busyflag = Thread.currentThread();
                break;
            }
        }
        try {
            Thread.sleep(100);
        } catch (Exception e) {}
    }
}

In this new implementation of the getBusyFlag() method, we only synchronized the period between checking the flag and setting it if it is not busy. This usage is very similar to the synchronized method usage, except that the scope during which the lock is held is much smaller.

Interestingly, this usage not only gives us more precise control over when the object lock is held, but it also allows us to select which object’s lock to grab. In this case, since we want to grab the same object lock as in the tryGetBusyFlag() and freeBusyFlag() methods, we chose this as the object on which to obtain the lock. For synchronized methods, the lock that is obtained is the object lock of the class in which the method exists; in other words, the this object.

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

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