/***************************************************************/
/* Prof. Dr. Carsten Vogt                                      */
/* FH Koeln, Fak. 07 / Nachrichtentechnik                      */
/* http://www.nt.fh-koeln.de/vogt                              */
/*                                                             */
/* wait(), notify():                                           */
/* Zwei Threads werden in eine Reihenfolge gebracht. Sie syn-  */
/* chronisieren sich dafuer ueber ein Objekt, in dessen Metho- */
/* den sie wait() bzw. notify() aufrufen.                      */
/***************************************************************/

class SynchronObjekt {

 /* Synchronisationsobjekt, mit dessen Hilfe sich zwei Threads
    in eine Reihenfolge bringen. */

 boolean stop;
  /* gibt an, ob der Nachfolgerthread warten muss */

 /* Konstruktor: setzt "stop" auf true */

 SynchronObjekt() {
  stop = true;
 }

 /* Die folgende Methode "warte()" versetzt den aufrufenden
    Thread in den Wartezustand, solange "stop" true ist.
    Der Uebergang in den Wartezustand erfolgt durch wait();
    wait() muss in einer als synchronized deklarierten Methode
    eines Objekts benutzt werden.
    Die Benutzung einer while-Schleife, um die Wartebedingung
    zu testen, ist guter Programmierstil. Es kann durchaus
    vorkommen (wenn auch nicht in dieser speziellen Anwendung),
    dass ein Thread geweckt wird, ohne dass die spezielle Be-
    dingung, auf die er eigentlich wartet, erfuellt ist. Er
    sollte sie daher nochmals testen und ggf. erneut in den
    Wartezustand uebergehen. */

 synchronized void warte() {
  while (stop) {
   try {
    wait();
    /* wait() blockiert den aufrufenden Thread so lange, bis ein
       anderer Thread in einer anderen Methode dieses Objekts
       notify() oder notifyAll() aufruft. Das ist in diesem Fall
       der Thread, der die Methode "weiter()" ausfuehrt. */
   } catch (InterruptedException E) { }
  }
  }

 /* Durch die Methode "weiter()" signalisiert der Vorgaenger-
    thread seinem Nachfolger, dass er weiterlaufen kann. Dies
    geschieht durch notify(), das wie wait() in einer als
    synchronized deklarierten Methode benutzt werden muss. */

 synchronized void weiter() {
  stop = false;
  /* Benachrichtige per notify() einen wartenden Thread. */
  notify();
 }

}

/* Vorgaenger-Thread */

class Vorgaenger extends Thread {

 private SynchronObjekt obj;
  /* Objekt, ueber das der Thread sich synchronisiert */

 Vorgaenger(SynchronObjekt o) {
  obj = o;
 }

 public void run() {
  try {
   System.out.println("Vorgaenger: Start"); 
   /* Erst 4 Sekunden warten ... */
   sleep(4000);
  /* ... und dann den Nachfolger benachrichtigen */
  System.out.println("Vorgaenger: SYNCHRONISATIONSPUNKT ERREICHT"); 
  obj.weiter();
  System.out.println("Vorgaenger: Weiterlaufen");
  sleep(2000);
  System.out.println("Vorgaenger: Ende"); 
  } catch (InterruptedException E) { }
 }

}

/* Nachfolger-Thread */

class Nachfolger extends Thread {

 private SynchronObjekt obj;
  /* Objekt, ueber das der Thread sich synchronisiert */

 Nachfolger(SynchronObjekt o) {
  obj = o;
 }

 public void run() {
  try {
   System.out.println("Nachfolger: Start"); 
   System.out.println("Nachfolger: Warten am Synchronisationspunkt"); 
   obj.warte();
   System.out.println("Nachfolger: WEITERLAUFEN AM SYNCHRONISATIONSPUNKT"); 
   sleep(3000);
   System.out.println("Nachfolger: Ende"); 
  } catch (InterruptedException E) { }
 }

}

public class WaitNotify {

 /* Hauptprogramm */

 public static void main(String[] args) {

  System.out.println();

  SynchronObjekt sobj = new SynchronObjekt();
  Vorgaenger t1 = new Vorgaenger(sobj);
  Nachfolger t2 = new Nachfolger(sobj);

  t1.start();
  t2.start();
  
 }

}

