// Fig. 13.5: SharedCell.java
// Show multiple threads modifying shared object.
// Use synchronization to ensure that both threads 
// access the shared cell properly.

public class SharedCell {
   public static void main( String args[] )
   {
      HoldInteger h = new HoldInteger();
      ProduceInteger p = new ProduceInteger( h );
      ConsumeInteger c = new ConsumeInteger( h );

      p.start();
      c.start();
   }
}

class ProduceInteger extends Thread {
   private HoldInteger pHold;

   public ProduceInteger( HoldInteger h )
   {
      pHold = h;
   }

   public void run()
   {
      for ( int count = 0; count < 10; count++ ) {
         // sleep for a random interval
         try {
            Thread.sleep( (int) ( Math.random() * 3000 ) );
         }
         catch( InterruptedException e ) {
            System.err.println( e.toString() );
         }

         pHold.setSharedInt( count );
         System.out.println( "Producer set sharedInt to " +
                             count );
      }

      pHold.setMoreData( false );
   }
}

class ConsumeInteger extends Thread {
   private HoldInteger cHold;

   public ConsumeInteger( HoldInteger h )
   {
      cHold = h;
   }

   public void run()
   {
      int val;

      while ( cHold.hasMoreData() ) {
         // sleep for a random interval
         try {
            Thread.sleep( (int) ( Math.random() * 3000 ) );
         }
         catch( InterruptedException e ) {
            System.err.println( e.toString() );
         }

         val = cHold.getSharedInt();
         System.out.println( "Consumer retrieved " + val );
      } 
   }
}

class HoldInteger {
   private int sharedInt = -1;
   private boolean moreData = true;
   private boolean writeable = true;

   public synchronized void setSharedInt( int val )
   {
      while ( !writeable ) {
         try {
            wait();
         }
         catch ( InterruptedException e ) {
            System.err.println( "Exception: " + e.toString() );
         }
      }

      sharedInt = val;
      writeable = false;
      notify();
   }

   public synchronized int getSharedInt()
   {
      while ( writeable ) {
         try {
            wait();
         }
         catch ( InterruptedException e ) {
            System.err.println( "Exception: " + e.toString() );
         }
      }

      writeable = true;
      notify();
      return sharedInt;
   }

   public void setMoreData( boolean b ) { moreData = b; }

   public boolean hasMoreData() { return moreData; }
}
