class myThread extends Thread {

   myThread() {} // default constructor

   private static long startTime = System.currentTimeMillis();

   protected static void nap(int ms) {
      try {
         sleep(ms);
      } catch (InterruptedException e) {
      }
   }

   protected static long age() {
      return System.currentTimeMillis()-startTime;
   }

   protected static int getarg(int i, int n, String args[]) {
      int value;
      try {
         value = Integer.parseInt(args[i-1]);
      } catch (ArrayIndexOutOfBoundsException e) {
         return n;
      } catch (NumberFormatException e) {
         return n;
      }
      return value;
   }

   protected static float getarg(int i, float f, String args[]) {
      float value;
      try {
         value = Float.valueOf(args[i-1]).floatValue();
      } catch (ArrayIndexOutOfBoundsException e) {
         return f;
      } catch (NumberFormatException e) {
         return f;
      }
      return value;
   }
}

class monitorShowSigDis extends myThread {

   monitorShowSigDis() {}

   public synchronized void gotoSleep(int id) {
      System.out.println
         ("Process "+ id +" enters monitor sleep proc at time "+ age());
      nap(100);
      System.out.println
         ("Process "+ id +" queues on c.v. sleep proc at time "+ age());
      try {wait();} catch (InterruptedException e) {}
      System.out.println
         ("Process "+ id +" awakes on c.v. sleep proc at time "+ age());
      nap(100);
      System.out.println
         ("Process "+ id +" leaves monitor sleep proc at time "+ age());
   }

   public synchronized void wakeUp(int id) {
      System.out.println
         ("Process "+ id +" enters monitor wake proc at time "+ age());
      nap(1000);
      System.out.println
         ("Process "+ id +" signals c.v in wake proc at time "+ age());
      notify();
      System.out.println
         ("Process "+ id +" continues  in  wake proc at time "+ age());
      nap(100);
      System.out.println
         ("Process "+ id +" signal two! in wake proc at time "+ age());
      notify();
      nap(100);
      System.out.println
         ("Process "+ id +" leaves monitor wake proc at time "+ age());
   }

}

class sleeperThread extends myThread {

   int id = 0;
   monitorShowSigDis mssd = null;

   private sleeperThread() {}
   sleeperThread(int id, monitorShowSigDis mssd) {
      this.id = id;
      this.mssd = mssd;
   }

   public void run() {
      nap(id*1000);
      System.out.println
         ("Sleeper "+ id +" wants to enter monitor sleep proc at time "+ age());
      mssd.gotoSleep(id);
      System.out.println
         ("Sleeper "+ id +" done at time "+ age());
   }
}

class wakerThread extends myThread {

   int id = 0;
   monitorShowSigDis mssd = null;

   private wakerThread() {}
   wakerThread(int id, monitorShowSigDis mssd) {
      this.id = id;
      this.mssd = mssd;
   }

   public void run() {
      nap(2500);
      System.out.println
         ("Waker 9 wants to enter monitor wake proc at time "+ age());
      mssd.wakeUp(9);
      System.out.println
         ("Waker 9 done at time "+ age());
   }
}

class driverShowSigDis {

/*
 * This example shows that Java wait/notify use the `signal and continue'
 * signaling discipline.  One may generate multiple signals inside a
 * monitor and continue executing inside the monitor until a return.
 * IMPORTANT NOTE: it is possible that a signaled process may not resume
 * inside the monitor until after another process that was blocked on a
 * method call enters and leaves (or waits inside) the monitor.  This
 * can be seen happening in the sample output shown below.  Sleeper 3
 * gets in the monitor before Sleepers 1 and 2 proceed after being
 * signaled.  Caveat programmer!
 */

   private static sleeperThread sT[];
   private static wakerThread wT = null;
   private static monitorShowSigDis mssd = null;

   public static void main(String arg[]) {
      mssd = new monitorShowSigDis();
      sT = new sleeperThread[3];
      for (int i = 0; i<3; i++) sT[i] = new sleeperThread(i+1, mssd);
      wT = new wakerThread(9, mssd);
      for (int i = 0; i<3; i++) sT[i].start();
      wT.start();
   }

}

/* ............... Example compile and run(s)

% $JAVAHOME/bin/javac showMonitorSigDis.java
% $JAVAHOME/bin/java driverShowSigDis
Sleeper 1 wants to enter monitor sleep proc at time 1051
Process 1 enters monitor sleep proc at time 1087
Process 1 queues on c.v. sleep proc at time 1200
Sleeper 2 wants to enter monitor sleep proc at time 2049
Process 2 enters monitor sleep proc at time 2058
Process 2 queues on c.v. sleep proc at time 2169
Waker 9 wants to enter monitor wake proc at time 2559
Process 9 enters monitor wake proc at time 2570
Sleeper 3 wants to enter monitor sleep proc at time 3059
Process 9 signals c.v in wake proc at time 3580
Process 9 continues  in  wake proc at time 3590
Process 9 signal two! in wake proc at time 3700
Process 9 leaves monitor wake proc at time 3819
Waker 9 done at time 3828
Process 3 enters monitor sleep proc at time 3837
Process 3 queues on c.v. sleep proc at time 3949
Process 1 awakes on c.v. sleep proc at time 3958
Process 1 leaves monitor sleep proc at time 4070
Sleeper 1 done at time 4081
Process 2 awakes on c.v. sleep proc at time 4089
Process 2 leaves monitor sleep proc at time 4199
Sleeper 2 done at time 4207
^C
                                              */
