CIS 260 Shared Resource Problems I

It's time for those problems that were mentioned earlier. If what we have covered was all there was to threads, we'd all use them and live happily ever after. There are some inherent problems with programs that run concurrently. We are about to explore one of them.

This example demonstrates problems when multiple threads both try to access the same variables. Threads may be implemented to timeslice. This means that a thread can be executing and suddenly be halted to allow another thread some time to execute. These abrupt stops can occur right in the middle of a method, leaving data in an unpredictable state.

What the following example and sample output show is two threads competing for access to the variables count1, count2, and count3. It would seem natural to assume that the variables should always be equal when TestSync checks them since they start off equal and are only incremented together on one line in Adder. That is not the case however. Adder takes its time to execute (mainly because of the call to sleep()) while TestSync runs wildly. Once in a while Adder is interrupted and paused in the middle of the line incrementing the counter variables. TestSync may be interrupted while fetching the values of the counter variables. Either way, they end up not matching.

I have dropped fancy user interfaces here because I want the code to more explicitly deal with just the threading. Notice how many times TestSync executed its loop (230,107,405) compared to Adder (101).

public class TThread5 {
   boolean done = false;
   private int count1 = 0, count2 = 0, count3 = 0;

   private void init() {
      class Adder extends Thread {
         public void run() {
            while (count1 <= 100) {
               count1++; count2++; count3++;
               try {
                  Thread.sleep(50);
               } catch (InterruptedException e) {
                  System.out.println("Application Interrupted");
               }
            }
            done = true;
         }
      }

      class TestSync extends Thread {
         private int totAccess = 0;

         public void run() {
            int c1, c2, c3;
            while (!done) {
               totAccess++;
               c1 = count1; c2 = count2; c3 = count3;
               if ((c1 != c2) || (c2 != c3))
                  System.out.println("\rNot synced: " + c1 +
                                     ", " + c2 + ", " + c3);
            }
            System.out.println("Total sync accesses: " + totAccess);
         }
      }

      (new Adder()).start();
      (new TestSync()).start();
   }

   public static void main(String[] args) {
      TThread5 app = new TThread5();
      app.init();
   }
}
// Not synced: 2, 3, 3
// Not synced: 7, 8, 8
// Not synced: 12, 13, 13
// Not synced: 16, 16, 17
//   ... more output ...
// Not synced: 90, 90, 91
// Not synced: 95, 95, 96
// Total sync accesses: 230107405

Previous: the Runnable interface

Next: shared resource problems II