Discussion of Activity 8.6 - Running the greedy consumers

Sample solution

Code for class SweetJar:

public class SweetJar
{
   private int numberOfSweets;
   private int turn = 1;

   public SweetJar(int sweets)
   {
      numberOfSweets = sweets;
   }

   public int countSweets()
   {
      return numberOfSweets;
   }

   public synchronized void eatSweet(int id)
   {
      try
      {
         // If this thread is attempting to go out of turn it must be blocked.
         while (turn != id)
         {
            wait();
         }
         /*  You may have used 'if' instead of 'while'.
             In general 'while' must be used - see Activity 8.8.
         */
      }
      catch(Exception e)
      {
         System.out.println(e);
      }
      System.out.println("Consumer " + id + " eats sweet "
                          + numberOfSweets + " - yummy!");
      numberOfSweets--;
      turn = 3 - turn;
      notifyAll();
   }
}

The solution as given above, allows the situation to occur where 'sweet 0 gets eaten'. To understand why this happens, we look at the while statement in the Consumer class. If there is currently 1 sweet left, a thread will be allowed to continue and invoke the eatSweet method. It may find it is not its turn, and waits. The next thread may check the while condition, find that there is 1 sweet left, and it also invokes the eatSweet method.

At this point there are 2 threads active. The one whose turn it is will actually continue, decrease the number of sweets, and notify other threads. This is where the problem arises, because a notified thread can proceed without having to recheck the while condition.

What can we do to prevent this? We must build in a further check, inside the eatSweet method so that the 'actual' eating of the sweet takes place within an if statement, as follows:

if (numberOfSweets > 0)
{
   System.out.println("Consumer " + id +  " eats sweet "
                       + numberOfSweets + " - yummy!");
   numberOfSweets--;
   turn = 3 - turn;
   notifyAll();
}
else
{
   System.out.println("no more sweets");
}