# Problem mit Thread (wait und notify)



## JohnWayne (22. Mai 2009)

Hallo und guten Abend (oder besser Morgen ) zusammen,

ich habe ein kleines Problem mit einer meiner Übungen zu Java.
Folgendes ist zu tun:

In einem Fenster soll sich ein Kreis animiert bewegen. Realisiert ist das Ganze durch einen Thread! Hier ein Codeschnipsel ....

    /**
     * Die run-Methode des Threads Hier wird die Animation ausgeführt.
     * run() wird indirekt durch start() aufgerufen. Darf niemals direkt auf
     * gerufen werden.
     */
    public void run() {
        Leinwand leinwand = Leinwand.gibLeinwand();
        int breite = leinwand.gibBreite();
        int hoehe = leinwand.gibHoehe();
        beendet = false;

        if(!istSichtbar) sichtbarMachen();

        while (!beendet) {

            // Ggf. an der Wand reflektieren, neue Position berechnen.
            if(xPosition + durchmesser + dx > breite || xPosition + dx < 0) dx = -dx;
            if(yPosition + durchmesser + dy > hoehe  || yPosition + dy < 0) dy = -dy;

            xPosition += dx;
            yPosition += dy;

            zeichnen(); // neu zeichnen
        }    

    }

    /**
     * Animation anhalten
     */

    public void anhalten() {
        beendet = true;
    }


Wir sollen nun realisieren, dass man den Thread mittels wait() und notify() anhält bzw. weiter laufen läßt! Ich habe schon mehrere Ansätze versucht aber ich komme nicht weiter bzw. finde meinen Fehler nicht ;(
Hier mal mein Ansatz:

    public synchronized void anhalten()
    {
        synchronized(this)
        {
            try
            {
                wait();
            }
        catch(InterruptedException e)
        {
        }
    }
}
    public synchronized void fortsetzen()
    {
        synchronized(this)
        {
            notify();
        }
    }

Sobald ich anhalten() aufrufe komme ich in eine Endlosschleife .... kann mir jemand helfen?


----------



## Murray (22. Mai 2009)

JohnWayne hat gesagt.:


> Sobald ich anhalten() aufrufe komme ich in eine Endlosschleife .... kann mir jemand helfen?


Das glaube ich nicht - eine Endlosschleife ist das nicht. Vielmehr wird der Thread solange "schlafengelegt", bis (aus einem anderen Thread) fortsetzen() aufgerufen wird. Wie sieht denn der Cdoe aus, mit dem du das testest?


----------



## JohnWayne (22. Mai 2009)

Hallo!

Also, der Sinn der anhalten() Methode besteht darin, den animierten Ball zu stoppen und mit fortsetzen() wieder zu animieren. 
Aber sobald ich anhalten aufrufe passiert einfach nichts ; der Ball läuft weiter ....

Die beiden Methoden sind in der selben Klasse wie run() ; ist das falsch ??? 
Wir arbeiten noch mit diesem BlueJ und ich als Benutzer muss die Methoden anhalten() und fortsetzen() selbst aufrufen. Es gibt keine main - Methode ...


----------



## JohnWayne (22. Mai 2009)

Ich nochmal 

wenn ich das Ganze so mache, dann läuft der Ball zwar weiter aber ich bekomme ne Exception geworfen (IllegalMonitorState) bei "t.wait" .... (t ist meine Variable für den Thread)


    public synchronized void anhalten()
    {
        synchronized(this)
        {
            try
            {
                t.wait();
            }
        catch(InterruptedException e)
        {
            System.out.println(e.getMessage());
        }
    }
}
    public synchronized void fortsetzen()
    {
        synchronized(this)
        {
            t.notify();
        }
    }

Hilfe ich komme nicht weiter ! ;(


----------



## Marco13 (22. Mai 2009)

Wait und notify müssen immer auf den objekten aufgerufen werden, auf die "synchronized" angewendet wurde. In diesem Fall müßtest du also an beiden Stellen "synchronized(t)" schreiben (vermutlich - wenn nicht, müßte ich nochmal nach mehr Code gucken)


----------



## JohnWayne (22. Mai 2009)

Nein, leider keine Änderung!

Hier mal der Code der gesamten Klasse:


```
import java.awt.geom.Ellipse2D;

/**
 * Ein Kreis, der manipuliert werden kann und sich selbst auf einer Leinwand
 * zeichnet.
 * 
 * @author Michael Kölling und David J. Barnes
 * @version 2006.03.30
 * 
 * Methoden animieren() und anhalten() ergänzt, um eine Animation des Kreises 
 * zu ermöglichen. 
 * In dieser Version Animation mit Thread. 
 * HHPaul 06/2007
 */

public class Kreis implements Runnable {

    Thread t = new Thread(this);
    private int durchmesser;

    private int xPosition;

    private int yPosition;

    private String farbe;

    private boolean istSichtbar;
    
    private boolean beendet = true;
    
    private int dx;
    private int dy;

    /**
     * Erzeuge einen neuen Kreis an einer Standardposition mit einer
     * Standardfarbe.
     */
    public Kreis() {
        durchmesser = 30;
        xPosition = 20;
        yPosition = 60;
        farbe = "rot";
        istSichtbar = false;
    }
    

    /**
     * Mache diesen Kreis sichtbar. Wenn es bereits sichtbar ist, tue nichts.
     */
    public void sichtbarMachen() {
        istSichtbar = true;
        zeichnen();
    }

    /**
     * Mache diesen Kreis unsichtbar. Wenn es bereits unsichtbar ist, tue
     * nichts.
     */
    public void unsichtbarMachen() {
        loeschen();
        istSichtbar = false;
    }

    /**
     * Bewege diesen Kreis einige Bildschirmpunkte nach rechts.
     */
    public void nachRechtsBewegen() {
        horizontalBewegen(20);
    }

    /**
     * Bewege diesen Kreis einige Bildschirmpunkte nach links.
     */
    public void nachLinksBewegen() {
        horizontalBewegen(-20);
    }

    /**
     * Bewege diesen Kreis einige Bildschirmpunkte nach oben.
     */
    public void nachObenBewegen() {
        vertikalBewegen(-20);
    }

    /**
     * Bewege diesen Kreis einige Bildschirmpunkte nach unten.
     */
    public void nachUntenBewegen() {
        vertikalBewegen(20);
    }

    /**
     * Bewege diesen Kreis horizontal um 'entfernung' Bildschirmpunkte.
     */
    public void horizontalBewegen(int entfernung) {
        loeschen();
        xPosition += entfernung;
        zeichnen();
    }

    /**
     * Bewege diesen Kreis vertikal um 'entfernung' Bildschirmpunkte.
     */
    public void vertikalBewegen(int entfernung) {
        loeschen();
        yPosition += entfernung;
        zeichnen();
    }

    /**
     * Bewege diesen Kreis langsam horizontal um 'entfernung' Bildschirmpunkte.
     */
    public void langsamHorizontalBewegen(int entfernung) {
        int delta;

        if (entfernung < 0) {
            delta = -1;
            entfernung = -entfernung;
        } else {
            delta = 1;
        }

        for (int i = 0; i < entfernung; i++) {
            xPosition += delta;
            zeichnen();
        }
    }

    /**
     * Bewege diesen Kreis langsam vertikal um 'entfernung' Bildschirmpunkte.
     */
    public void langsamVertikalBewegen(int entfernung) {
        int delta;

        if (entfernung < 0) {
            delta = -1;
            entfernung = -entfernung;
        } else {
            delta = 1;
        }

        for (int i = 0; i < entfernung; i++) {
            yPosition += delta;
            zeichnen();
        }
    }
    

    /**
     * Ändere den Durchmesser dieses Kreises in 'neuerDurchmesser' (Angabe in
     * Bildschirmpunkten). 'neuerDurchmesser' muss größer gleich Null sein.
     */
    public void groesseAendern(int neuerDurchmesser) {
        loeschen();
        durchmesser = neuerDurchmesser;
        zeichnen();
    }

    /**
     * Ändere die Farbe dieses Kreises in 'neueFarbe'. Gültige Angaben sind
     * "rot", "gelb", "blau", "gruen", "lila" und "schwarz".
     */
    public void farbeAendern(String neueFarbe) {
        farbe = neueFarbe;
        zeichnen();
    }

    /**
     * Zeichne diesen Kreis mit seinen aktuellen Werten auf den Bildschirm.
     */
    private void zeichnen() {
        if (istSichtbar) {
            Leinwand leinwand = Leinwand.gibLeinwand();
            leinwand.zeichne(this, farbe, new Ellipse2D.Double(xPosition,
                    yPosition, durchmesser, durchmesser));
            leinwand.warte(10);
        }
    }

    /**
     * Lösche diesen Kreis vom Bildschirm.
     */
    private void loeschen() {
        if (istSichtbar) {
            Leinwand leinwand = Leinwand.gibLeinwand();
            leinwand.entferne(this);
        }
    }

    /**
     * Animieren. Bewege diesen Kreis in der angebenen Richtung, bis er den Rand 
     * der Zeichenfläche erreicht.
     * @param dx Schrittweite in x-Richtung
     * @param dy Schrittweite in y-Richtung
     */
    
    public void animieren(int dx, int dy) {
        this.dx = dx;
        this.dy = dy;
        t.start();
    }
    
   
    
    /**
     * Die run-Methode des Threads Hier wird die Animation ausgeführt.
     * run() wird indirekt durch start() aufgerufen. Darf niemals direkt auf
     * gerufen werden.
     */
    public void run() {
        Leinwand leinwand = Leinwand.gibLeinwand();
        int breite = leinwand.gibBreite();
        int hoehe = leinwand.gibHoehe();
        beendet = false;
        
        if(!istSichtbar) sichtbarMachen();
        
        synchronized(this)
        {
        while(true)
        {
            // Ggf. an der Wand reflektieren, neue Position berechnen.
            if(xPosition + durchmesser + dx > breite || xPosition + dx < 0) dx = -dx;
            if(yPosition + durchmesser + dy > hoehe  || yPosition + dy < 0) dy = -dy;

            xPosition += dx;
            yPosition += dy;
        
            zeichnen(); // neu zeichnen
        }   
    }
        
    }
    
//     /**
//      * Animation anhalten
//      */
//     
//     public void anhalten() {
//         beendet = true;
//     }

    public synchronized void anhalten()
    {
        synchronized(t)
        {
            try
            {
                t.wait();
            }
        catch(InterruptedException e)
        {
            System.out.println(e.getMessage());
        }
    }
}
    public synchronized void fortsetzen()
    {
        synchronized(t)
        {
            t.notify();
        }
    }

}
```


----------



## Marco13 (22. Mai 2009)

Bei 
synchronized(t) { t.notify() }
kann eigentlich keine IllegalMonitorStateException geworfen werden. Kannst du ein compilierbares Beispiel posten, wo der fehler auftritt?


----------



## JohnWayne (22. Mai 2009)

Hier der Fehler ... ich weiss absolut nicht weiter !


----------



## Marco13 (22. Mai 2009)

DORT steht (im Gegensatz zu dem, was du geposted hast) NICHT
t.wait();
sondern nur
wait();
was soviel bedeutet wie "this.wait()", d.h. man müßte 
- entweder ein synchronized(this) darum machen, oder (was hier fast das gleiche wäre)
- die Methode als "private *synchronized* void ..." schreiben.


----------



## JohnWayne (22. Mai 2009)

Hallo!

Entschuldigung, mein Fehler! Ich habe das auch schon mit einem synchronized(this) versucht und t.wait() und synchronized im Methodenkopf und andere Sachen! Egal was ich mache es ändert sich einfach nicht! Das Bild von mir war der letzte Stand den ich versucht habe... sorry nochmal! Der kreis läuft weiter! Der einzige Unterschied ist, das ich keine Exception mehr bekomme ... alles Möglichkeiten die ich schon versucht habe aber dieser sch... Ball läuft weiter  Meine Nerven .... ohje 

Ich hab mal eine Datei mit dem Projekt beigelegt. Vielleicht gibt es ja jemanden der sich das mal anschauen kann. Ich bin wirklich nicht zu faul um zu suchen aber 2 meiner Kollegen und ich finden einfach den Fehler nicht!

PS: Da ist noch ne Klasse Dreieck und Quadrat drin, die sind erstmal nicht wichtig. Vorrangig ist erstmal, dass ich den Fehler finde warum der Kreis nicht stoppt!

Danke im Voraus, der sich die Mühe macht!!!!!


----------



## Marco13 (23. Mai 2009)

Hm. Ich kann die nicht öffnen. Scheint kaputt zu sein.


----------



## JohnWayne (23. Mai 2009)

Neuer Versuch


----------



## Marco13 (23. Mai 2009)

Öffnen kann man die jetzt - aber sowas "main"-artiges hab ich da jetzt nicht drin gefunden...


----------



## JohnWayne (23. Mai 2009)

bei diesem programm gibt es keine main. wir erzeugen objekte manual. der umstieg auf eclipse erfolgt erst später ....


----------



## Marco13 (23. Mai 2009)

Und wie soll man das Problem dann nachvollziehen? 

Wie gesagt, abgesehen von der Regel "Nur auf Objekten wait und notify machen, auf die auch ge'synchronized' ist" weiß ich nicht, was ich noch sagen soll...


----------

