A Class to Perform Synchronization

Why do we need a new keyword to solve a race condition? Could we reengineer our algorithms so that race conditions do not exist? Let’s see if we can reengineer the AsyncReadSocket class not to have a race condition by using trial and error (obviously not the best programming technique, but one that is useful for our purposes). We’ll conclude that it is impossible to solve a race condition without direct support from the virtual machine, because everything that we might try requires two operations: testing and setting variable. Without some process in the virtual machine to ensure that nothing happens to the variable after it is tested and before it is set, a race condition can occur. But the investigation into a possible way to avoid the race condition will provide us with an important tool for our later use—the BusyFlag class.

At first glance, the easiest way to make sure that the two threads do not try to change the result variable, or any buffer at the same time, is to use the concept of a busy flag: if a thread needs to access the result variable, it must set the flag to busy. If the flag is already busy, the thread must wait until the flag is free, at which point it must set the flag to busy. The thread is then free to access the buffer without fear of it being accessed by the other thread. Once the task is completed, the thread must set the flag to not busy.

Here’s a possible implementation of the busy flag:

public class BusyFlag {
    protected Thread busyflag = null;

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

    public void freeBusyFlag () {
        if (busyflag == Thread.currentThread()) {
            busyflag = null;
        }
    }
}

This BusyFlag class contains two methods. The method getBusyFlag() sits in a loop until it is able to set the busyflag to the current thread. As long as the busyflag is set to another thread, our thread waits for 100 milliseconds. As soon as the flag is set to null, our thread sets it to the current thread. The other method, freeBusyFlag() , frees the flag by setting it back to null. This implementation seems to solve the problem simply and elegantly. But it does not.

Why do we need to sleep for 100 milliseconds? Because there seems to be no way to detect changes in the flag without a polling loop. However, a polling loop that does not sleep() simply wastes CPU cycles that can be used by other threads. At the other extreme, it takes a minimum of 100 milliseconds to set the busy flag even if no thread is holding the flag in the first place. A possible enhancement that addresses this problem may be as simple as making the sleep time a variable, but no matter what we configure the time to be, we will be balancing whether we want to be able to set the flag in a decent amount of time versus the CPU cycles wasted in a polling loop.

Why do we sleep for 100 milliseconds even if the flag is not set? This is actually intentional. There is a race condition between the check to see if the flag is null and setting the flag. If two threads find that the flag is free, they can each set the flag and exit the loop. By calling the sleep() method, we allow the two threads to set busyflag before checking it again in the while loop. This way, only the second thread that sets the flag can exit the loop, and hence exit the getBusyFlag() method.

Of course, this is still a problem. As unlikely as it seems, it is still possible that this order of execution might occur:

  1. Thread A detects that the busyflag is free.

  2. Thread B detects that the busyflag is free.

  3. Thread B sets the busyflag.

  4. Thread B sleeps for 100 milliseconds.

  5. Thread B wakes up, confirms that it has the busyflag, and exits the loop.

  6. Thread A sets the busyflag, sleeps, wakes up, confirms it has the busyflag, and exits the loop.

This is an extremely unlikely occurrence, but possible nonetheless; hence, this code is not one that most programmers are willing to accept.

We could use the BusyFlag class to replace the synchronized method in our Account class like this:

public class Account {
    private float total;
    private flag = new BusyFlag();

    public boolean deduct(float t) {
        boolean succeed = false;
        flag.getBusyFlag();
        if (t <= total) {
            total -= t;
            succeed = true;
        }
        flag.freeBusyFlag();
        return succeed;
    }
}

The vast majority of the time, this BusyFlag class works. However, even if you ran a huge beta test across 100 bank ATMs for a period of one year without a single problem, would you be willing to bet your career on a AutomatedTeller class that uses our BusyFlag class?

What if multiple threads set the busyflag at the same moment? Is the act of setting the busyflag variable atomic? The Java specification guarantees that setting any variable other than a double or a long is atomic, so in this example, it does not matter if multiple threads attempt to set the flag at the same moment. In the case where two threads are setting a long or a double, however, it is possible that the variable will be set incorrectly: part of the variable will contain the bits set by the first thread and the rest of the variable will contain the bits set by the second thread. However, atomicity does not insure thread communication; see the discussion of volatile in Appendix A.

Can we fix our BusyFlag class with the synchronization primitives? The problems that we encountered in the BusyFlag class are the same problems the BusyFlag class was meant to solve in the first place. This means that we can fix the problems in the BusyFlag class by using the synchronization primitives; we could use the BusyFlag class to solve other race conditions without worrying that it might break under certain conditions. The implementation (still not optimal) that solves this problem follows:

public class BusyFlag {
     protected Thread busyflag = null;
     public void getBusyFlag() {
          while (tryGetBusyFlag() == false) {
               try {
                    Thread.sleep(100);
               } catch (Exception e) {}
          }
     }
     public synchronized boolean 
            
            tryGetBusyFlag() {
          if (busyflag == null) {
               busyflag = Thread.currentThread();
               return true;
          }
          return false;
     }

     public synchronized void freeBusyFlag() {
          if (busyflag == Thread.currentThread()) {
               busyflag = null;
          }
     }
}

In this implementation of the BusyFlag class, we introduced a new method called tryGetBusyFlag(). It is essentially the same as the getBusyFlag() method except that it does not wait until the flag is free. If the flag is free, it sets the flag and returns true. Otherwise it returns false. You’ll notice that this method is declared as synchronized. This means the system makes sure the thread that makes the call to the tryGetBusyFlag() method has grabbed the object lock during the execution of the method.

The freeBusyFlag() method is also declared as synchronized: the thread that made the method call must also grab the object lock before it can continue. Since there is only one object lock for each instance of the class, the lock that freeBusyFlag() will try to grab is the same lock tryGetBusyFlag() will grab. This means that there will be no race condition between threads trying to get the busyflag and the thread that frees the busyflag.

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

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