Problem mit Thread (wait und notify)

Status
Nicht offen für weitere Antworten.

JohnWayne

Mitglied
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

Top Contributor
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

Mitglied
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

Mitglied
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

Top Contributor
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

Mitglied
Nein, leider keine Änderung!

Hier mal der Code der gesamten Klasse:

Code:
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

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

Marco13

Top Contributor
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

Mitglied
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!!!!!
 

Anhänge

  • Hausarbeit6.zip
    22,3 KB · Aufrufe: 3

JohnWayne

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

Marco13

Top Contributor
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...
 
Status
Nicht offen für weitere Antworten.

Neue Themen


Oben