/*************************************************************/
/* Prof. Dr. Carsten Vogt                                    */
/* FH Koeln, Fak. 07 / Nachrichtentechnik                    */
/* http://www.nt.fh-koeln.de/vogt                            */
/*                                                           */
/* wait(), notify():                                         */
/* Ein Objekt bietet zwei Methoden an, wobei sichergestellt  */
/* wird, dass zunaechst die eine und erst dann die andere    */
/* ausgefuehrt wird.                                         */
/*************************************************************/

class SynchronObjekt {

 /* Objekt, das u.a. zwei Methoden "zuerst()" und "spaeter()"
    anbietet. Es wird durchgesetzt, dass "spaeter()" nicht vor
    "zuerst()" ausgefuehrt wird. */

 boolean fertig;
  /* gibt an, ob "zuerst()" schon ausgefuehrt wurde. */

 /* Konstruktor: setzt "fertig" auf false */

 SynchronObjekt() {
  fertig = false;
 }

 /* Die folgende Methode "warte()" versetzt den aufrufenden
    Thread in den Wartezustand, solange "fertig" false 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 (!fertig) {
   try {
    wait();
    /* wait() blockiert den aufrufenden Thread (in diesem Pro-
       gramm den, der "spaeter()" ausfuehrt) 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 "zuerst()" ausfuehrt. */
   } catch (InterruptedException E) { }
  }
  }

 /* Die Methode "fertig()" signalisiert, dass die erste Methode
    "zuerst()" beendet ist. Dies geschieht durch notify(), das
    wie wait() in einer als synchronized deklarierten Methode
    benutzt werden muss. */

 synchronized void fertig() {
  fertig = true;
  /* Benachrichtige per notify() einen wartenden Thread - in
     diesem Programm den, der in "spaeter()" und "warte()"
     blockiert ist. */
  notify();
 }

 /* zuerst(): Methode, die als erste auf dem Objekt ausgefuehrt werden soll */
 
 void zuerst(Thread t) {
  System.out.println("Methode 1: Beginn");
  /* Ausfuehrungszeit: ca. 2 Sekunden */
  try {
   t.sleep(2000);
  } catch (InterruptedException E)
     {  }
  System.out.println("Methode 1: Ende");
  /* Signalisierung, dass erste Methode beendet ist */
  fertig();
 }

 /* spaeter(): Methode, die als zweite auf dem Objekt ausgefuehrt werden soll */
 
 void spaeter(Thread t) {
  try {
   System.out.println("Methode 2: Beginn mit Wartezustand");
   /* Abwarten, bis erste Methode beendet */   
   warte();
   System.out.println("Methode 2: Wartezustand verlassen");
   /* Ausfuehrungszeit: ca. 2 Sekunden */
   t.sleep(2000);
   System.out.println("Methode 2: Ende");
  } catch (InterruptedException E) { }
 }

}

/* Thread, der die erste Methode aufruft */

class Vorgaenger extends Thread {

 private SynchronObjekt obj;

 Vorgaenger(SynchronObjekt o) {
  obj = o;
 }

 public void run() {
  try {
   /* Erst 2 Sekunden warten ... */
   sleep(2000);
  } catch (InterruptedException E) { }
  /* ... und dann die Methode "zuerst()" aufrufen */
  obj.zuerst(this);
 }

}

/* Thread, der die zweite Methode aufruft */

class Nachfolger extends Thread {

 private SynchronObjekt obj;

 Nachfolger(SynchronObjekt o) {
  obj = o;
 }

 public void run() {
  /* sofort die Methode "spaeter()" aufrufen */
  obj.spaeter(this);
 }

}

public class WaitNotify2 {

 /* Hauptprogramm */

 public static void main(String[] args) {
 
  System.out.println();

  /* sobj ist das Objekt, dessen Methoden aufgerufen werden
     sollen. Es wird an die Konstruktoren der beiden Threads
     uebergeben. */
  SynchronObjekt sobj = new SynchronObjekt();
  Vorgaenger t1 = new Vorgaenger(sobj);
  Nachfolger t2 = new Nachfolger(sobj);

  /* Threads werden gestartet. Obwohl t1 zunaechst um zwei
     Sekunden verzoegert wird (siehe Class "Vorgaenger" oben),
     fuehrt er als erster seine Methode aus. */
  t1.start();
  t2.start();
  
 }

}

