Announcement

Collapse
No announcement yet.

wait / notify Verständnisfrage

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • wait / notify Verständnisfrage

    Hallo Liste,
    <br>
    ich hab jetzt schon einiges von Threads gelesen und herumexperimentiert, aber so meine Probleme mit wait bzw. notify.
    <br>
    Ich nehme hier ein Beispiel vom Web bei dem ich meine Verständnisfrage erläutere.
    <br>
    http://www.ccs.neu.edu/home/kenb/3338/tutorial.html
    <br>
    public class SharedStack
    {
    private List stack = new ArrayList();

    /**
    * Construct a new shared stack.
    */
    public SharedStack() {}

    /**
    * Push a new object onto a shared stack.
    * @param object The object to be pushed onto the stack.
    */
    public synchronized void produce(Object object) {
    stack.add(object);
    notify();
    }
    /**
    * Pop the top object off a stack or wait, if needed, until there is one.
    * @return The top object on the stack.
    */
    public synchronized Object consume() {
    while (stack.isEmpty()) {
    try {
    wait();
    } catch (InterruptedException e) {
    }
    }
    int lastElement = stack.size() - 1;
    Object object = stack.get(lastElement);
    stack.remove(lastElement);
    return object;
    }
    /**
    * Construct a string that represents a shared stack.
    * @return The string representation of a shared stack.
    */
    public synchronized String contains() {
    String s = "The shared stack currently contains the following:\n";
    for (Iterator i = stack.iterator(); i.hasNext(); ) {
    s += i.next() + "\n";
    }
    return s;
    }
    }

    Ich denke, daß ich dieses Beispiel an und für sich verstehe, aber im Bezug mit anderen Klassen sieht es nicht mehr so gut aus.<br>

    Wenn ich jetzt einen Thread starte, welcher die consume Funktion aufruft, dann laufe ich in den wait status ( was ich auch erwartet habe). Starte ich jetzt einen 2. Thread welcher (bezogen auf das selbe SharedStack Objekt) produce aufruft und somit auch notify sendet, dann hätte ich erwartet, daß der erste Thread (irgendwann) den wait status verläßt und mir den letzten Wert vom Stack liefert.
    <br>
    Dies kann ich aber nicht nachvollziehen???
    <br>
    Was ich auch noch nicht verstanden habe.
    <br>
    Das notify wird von einer Instanz des Objekt X gesendet und irgend eine Instanz des Objekt X welches im wait Status ist wird dann aus dem wait herausfallen und weiterlaufen?
    <br>
    Ich hoffe ich habe mich verständlich erklärt.
    Es wäre nett, wenn mich jemand aufklären könnte.
    <br>
    lg Dietmar

  • #2
    hallo dietmar,

    ich weiß nicht ob ich dich richtig verstanden habe, aber prinzipell ist es so:

    Thread A macht auf dem Stack ein consum. Da dieser noch leer ist geht er über das wait schalfen. Normalerweise sollte er jetzt pennen, bis er mal wieder von jemandem wachgemacht wird, dies ist in Java soviel ich weiß aber nicht genau definiert.Sagt jetzt ein Thread B: ok ich habe was drauf gelegt, an jeden den es interessiert: "Aufwachen". So jetzt werden durch das BS/die JVM nacheindender alle Schläfer geweckt (das heißt nicht das der zuerst pennen geganngen ist auch wirklich als erster wieder wach wird, man hofft, weiß es aber nicht so genau). Wenn A jetzt also aus dem wait aufwacht, schaut es erst mal ob tatsächlich der Stack einen Wert enthält. Hat ihm den jemand weggeschnappt, dann geht er wieder schlafen, ansonsten darf er jetzt an den Stack. Da die Methode synchronized ist, ist sichergestellt das auch unter Garantie er der einzige ist, der jetzt an dem Stack rumfummeln kann.
    Im Prinzip ist dies eine Art bussy waiting mit CPU Abgabe.
    Wenn er wieder wach ist, nimmt er das oberste Element vom Stapel (also wenn in der Zwischenzeit fünf draufgelegt wurden dann das fünfte und nicht etwa das erste) und freut sich und macht mit seiner Sache weiter.

    Naja und die produce Methode macht nix weiter als sich den Stack exklusiv sichern, ein Element drauf legen und jedem den es interessiert (also alle die schlafen und warten und schlafen und warten ...) zu sagen das er jetzt was draufgelegt hat.

    Verständlich

    Comment


    • #3
      Hallo Jan,

      ich glaube wir haben uns verstanden, aber in meinen Tests ist der "Schläfer" nie aufgewacht!

      Ich habe hier ein Beispiel mit dem ich so umhergefummelt habe. Bitte achte nicht auf dem Programmierstil!

      public class Starter1 implements Runnable
      {

      public SharedStack ss = new SharedStack();
      public boolean consume = false;

      public void run ()
      {
      if (consume)
      {
      System.out.println(ss.consume());
      try{ Thread.sleep(1000);}
      catch (InterruptedException ie){}
      }
      else
      {
      int i = 0;
      while (true)
      {
      ss.produce(Integer.toString(i++));
      System.out.println(ss.contains());
      try{ Thread.sleep(5000);}
      catch (InterruptedException ie){}
      }
      }
      }

      public static void main ( String [] args )
      {
      Starter1 ss2 = new Starter1();
      ss2.consume = true;
      Thread t2 = new Thread(ss2);
      t2.start();

      try{Thread.sleep(2000);}
      catch (InterruptedException ie){}

      Starter1 ss1 = new Starter1();
      ss2.consume = false;
      Thread t1 = new Thread(ss1);
      t1.start();
      }
      <br>
      Ich starte 2 Threads wobei der 1. erzeugt Thread das consume aufruft und in den wait rennt. Dann habe ich ein sleep gemacht, damit es sehr warscheinlicht ist, daß der Thread wirklich rennt.
      Dann ein 2. Thread mit produce.
      <br>
      In diesem Beispiel auf meiner Umgebung wird der 1. Thread nicht mehr aktiv. Ich weiß nicht, ob dies ein Problem vom Scheduler ist oder ob ich einfach etwas falsch mache (was ich auch vermute)

      lg Dietma

      Comment


      • #4
        hi again,

        ich bin mir gerade nicht so sicher ob das so geht wie du es willst. ich würde sagen du müßtes 2 objekte anlegen, zb. vom typ MyThread (welche Threads sind), denen du sagst im Konstruktor:

        a) bist du Consumer oder Producer
        b) mußt du den Stack mit übergeben

        Der Stack an sich ist eine ganz normal datenstruktur, mit der was gemacht wird, ist also selbst kein thread. denn so wie du es jetzt gestaltet hast weiß ich nicht ob du nicht dir selbst in die füße schießt ala ich gehe jetzt schlafen und mache mich dann wach (wofür dein fehlerbild spricht)

        Comment


        • #5
          Hi,
          ich glaube Du hast 2 verschiedene Stacks in deinem Programm, beide
          Threads sollten sich aber einen Stack teilen. Du legst die Stacks bei der
          initialisierung der Starter1 Objekte an. Dein SharedStack ist damit nicht
          shared ;-)

          Hier ein lauffähiges Beispiel, das funktioniert: In der main() Methode wird
          genau 1 SharedStack, 3 Consumer Threads und 3 Producer Threads
          angelegt, dann werden die Threads gestartet.

          -------------------------------------------------

          import java.util.ArrayList;
          import java.util.Iterator;
          import java.util.List;

          public class SharedStack {
          private List stack = new ArrayList();

          /**
          * Construct a new shared stack.
          */
          public SharedStack() {
          }

          /**
          * Push a new object onto a shared stack.
          *
          * @param object
          * The object to be pushed onto the stack.
          */
          public synchronized void produce(Object object) {
          stack.add(object);
          notify();
          }

          /**
          * Pop the top object off a stack or wait, if needed, until there is one.
          *
          * @return The top object on the stack.
          */
          public synchronized Object consume() {
          while (stack.isEmpty()) {
          try {
          wait();
          } catch (InterruptedException e) {
          }
          }
          int lastElement = stack.size() - 1;
          Object object = stack.get(lastElement);
          stack.remove(lastElement);
          return object;
          }

          /**
          * Construct a string that represents a shared stack.
          *
          * @return The string representation of a shared stack.
          */
          public synchronized String contains() {
          String s = "The shared stack currently contains the following:\n";
          for (Iterator i = stack.iterator(); i.hasNext() {
          s += i.next() + "\n";
          }
          return s;
          }

          /**
          * @param args
          */
          public static void main(String[] args) {
          final SharedStack stack = new SharedStack();

          Runnable consumer = new Runnable() {

          public void run() {
          for (int i = 0; i < 100; i++) {
          Integer integer = (Integer) stack.consume();

          System.out.println(Thread.currentThread().getName( ) + " consumed " +
          integer.intValue());
          }
          }
          };

          Runnable producer = new Runnable() {

          public void run() {
          for (int i = 0; i < 100; i++) {
          stack.produce(new Integer(i));
          }
          }
          };

          new Thread(consumer, "Consumer 1").start();
          new Thread(consumer, "Consumer 2").start();
          new Thread(consumer, "Consumer 3").start();
          new Thread(producer, "Producer 1").start();
          new Thread(producer, "Producer 2").start();
          new Thread(producer, "Producer 3").start();
          }

          }

          Comment


          • #6
            Hallo,

            das ist's gewesen. Ich habe 2 Stackobjekte gehabt! Wenn ich den Stack mitübergeben habe, dann hat's funktioniert. Super und vielen Dank!

            Aber nochmals zum Verständnis.

            Aus einem meiner Bücher habe ich es wie folgt verstanden:

            Jedes Objekt(?Klasse) hat einen Pool in dem die Objekte registriert sind die im wait zustand sind. Wird jetzt ein notify gesendet, dann wird ein x beliebiges Objekt aus dem Pool genommen und fällt aus dem wait Status heraus. Bei einem notifyAll fallen alle aus dem wait Status heraus.

            Was ich aber nicht verstehe ist: wenn ich 10 Objekte der Klasse A im wait Zustand habe (instanziert von meiner Applikation von verschiedensten Threads...) und einen notify sende, dann fällt ein Objekt aus dem wait hreaus und beginnt dann (irgend wann) mit der Weiterausführung vom Code. Dies ist dann in meinem hier konstruierten Beispiel irgend ein Thread in der App?

            Ich glaube ich habe hier Probleme mit Objekt, Klasse und mehreren Objekte der selben Klasse.

            Hat hier jemand aufklärende Worte?

            Nochmals Danke an Jan und Alexander

            lg Dietma

            Comment


            • #7
              hallo dietmar,

              genau das ist das problem bei den threads und java. du weißt nicht wer wach wird, daher muß der thread wenn er wach wird schauen ob auch alle bedingungen die er brauch erfüllt sind, ansonsten muß er sich wieder schlafen legen und gegebenen falls jemand anderes wecken. von welchen typ das threadobjekt ist, ist eigentlich egal, soviel ich weiß, das aufwachproblem existiert immer.

              gru&#223

              Comment


              • #8
                Dann hab ich's richtig verstanden, daß bei einem notify ein Objekt aller Instanzen von diesem Objekt (egal bei welchem Thread diese Instanz gebildet wurde) aus dem wait herausfällt?

                lg Dietma

                Comment


                • #9
                  Also ich stelle mir das so vor:
                  Einer der Threads, welche gerade ein wait() auf dem synchronisierten
                  Objekt machen, wird bei einem notify() auf dem synchronisierten Objekt
                  aufgeweckt. Welcher der wartenden Threads das ist, steht aber nicht fest

                  Comment


                  • #10
                    Hallo,
                    <br>
                    ich habe jetzt ein sehr vielsagendes Beispiel gefunden.
                    <br>

                    import java.util.Vector;

                    class Erzeuger extends Thread
                    {
                    static final int MAXQUEUE = 13;
                    private Vector nachrichten = new Vector();
                    public void run()
                    {
                    try
                    {
                    while ( true )
                    {
                    sendeNachricht();
                    sleep( (int)(Math.random()*100) );
                    }
                    } catch ( InterruptedException e ) { }
                    }
                    public synchronized void sendeNachricht()
                    throws InterruptedException
                    {
                    while ( nachrichten.size() == MAXQUEUE )
                    wait();
                    nachrichten.addElement( new java.util.Date().toString() );
                    notifyAll(); // oder notify();
                    }
                    // vom Verbraucher aufgerufen
                    public synchronized String getMessage()
                    throws InterruptedException
                    {
                    while ( nachrichten.size() == 0 )
                    wait();
                    notifyAll();
                    String info = (String) nachrichten.firstElement();
                    nachrichten.removeElement( info );
                    return info;
                    }
                    }
                    class Verbraucher extends Thread
                    {
                    Erzeuger erzeuger;
                    String name;
                    Verbraucher( String name, Erzeuger erzeuger )
                    {
                    this.erzeuger = erzeuger;
                    this.name = name;
                    }
                    public void run()
                    {
                    try
                    {
                    while ( true )
                    {
                    String info = erzeuger.getMessage();
                    System.out.println( name +" holt Nachricht: "+ info );
                    sleep( (int)(Math.random()*2000) );
                    }
                    } catch ( InterruptedException e ) { }
                    }
                    }
                    public class ErzeugerVerbraucherDemo
                    {
                    public static void main( String args[] )
                    {
                    Erzeuger erzeuger = new Erzeuger();
                    erzeuger.start();
                    new Verbraucher( "Eins", erzeuger ).start();
                    new Verbraucher( "Zwei", erzeuger ).start();
                    new Verbraucher( "Drei", erzeuger ).start();
                    }
                    }
                    <br>
                    Und somit ist es für mich, daß sich das wait / notify(all) immer auf ein shared Object bezieht!
                    <br>
                    lg Dietma

                    Comment


                    • #11
                      ps.: nur zur Vollständigkeit. Die Quelle ist "Java ist auch eine Insel&quot

                      Comment

                      Working...
                      X