# thread.sleep Sprung Problem



## Tossel (14. Mrz 2009)

hi,
ich bin gerad dabei eine art super mario zu programmieren. Das links und rechts laufen geht und auch der hintergrund verschiebt sich. allerdings habe ich ein problem den guten Mario springen zu lassen. Habe im keylistener bei der überprüfung ob die "hoch taste" vk_up gedrückt wurde eine schleife eingebaut die 10 mal durchlaufen wird und nach jedem schritt jeweils 20 millisekunden schlafen sollte und die mario-position um 2 verändern soll. was passiert nun? mario wird um 20 verschoben aber ohne die pausen darin!? das problem ist anscheinend, dass das system erst am ende der schleife repainted.

```
public void jump()
  {
    for (int i = 0; i < 10; i++) {
              try {
                    Thread.sleep(20);
                    ref.repaint();
                  }
              catch (InterruptedException e) {}
             
            ref.bmario_rj = true;
            ref.mposy = ref.mposy-2;
                                 }

  }
```
hoffe auf kompetente hilfe


----------



## MiDniGG (14. Mrz 2009)

[HIGHLIGHT="Java"]
//Der Thread
new Thread(new Runnable() {
     public void run() {
          int counter = 0;
          while(counter < 10) {
               sleep(20);
               counter++;
          }
     }
}).start();

//SleepMethode
private void sleep(long millis) {
     try {
          Thread.sleep(millis);
     } catch (InterruptedException e) {
          e.printStackTrace();
     }
}
[/HIGHLIGHT]

Edit: Ups vergessen was dazu zu schreiben ^^
Also wie Du siehst würde ich den Thread so extra starten wie oben und die sleep Methode auslagern. Ist schöner finde ich


----------



## Tossel (14. Mrz 2009)

Ja stimmt schoener ist es aber selber fehler xD


----------



## MiDniGG (14. Mrz 2009)

Dann solltest Du mehr zeit einstellen, da 20 millisekunden schwer wahrnehmbar sind 

Edit: Fang am Besten mal mit ner Sekunde (1000) an und mach die Zahl dann solang kleiner bis es perfekt is ^^


----------



## Tossel (14. Mrz 2009)

alles schon ausprobiert, er braucht dann halt nur länger bis er ganz oben erscheint also 10x 1 Sekunde ^^


----------



## SlaterB (14. Mrz 2009)

hast du das auch genau so mit dem Thread gemacht?
ohne Thread ist nämlich klar, dass die GUI nach Tastendruck blockiert, mit ist es nicht mehr klar,

dann musst du ein Testprogramm posten


----------



## Tossel (14. Mrz 2009)

thr.java

[HIGHLIGHT="Java"]import java.io.*;
public class thr  extends Thread
{


  main ref;

  public thr(main r)
  {
   ref = r;
  }

  public void jump()
  {
    for (int i = 0; i < 10; i++) {
              try {
                    Thread.sleep(20);

                  }
              catch (InterruptedException e) {e.printStackTrace();}

            ref.bmario_rj = true;
            ref.mposy = ref.mposy-2;
            ref.repaint();
                                 }

  }

}[/HIGHLIGHT]


main.java

[HIGHLIGHT="Java"]import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

import java.io.*;


public class main
extends JFrame
implements KeyListener
{
  // Anfang Variablen
  double vergleich = -1300;

  int mposx=10;
  int mposy=157;
  int gposx=60;
  int cposx=120;
  int bposx=1;
  int geschwindigkeit=2;
  boolean bmario_rw;
   boolean bmario_rj;
  boolean bmario_lw;
  boolean bmario_ls;
  boolean bmario_rs = true;
  int x=1;
  JPanel panel = new JPanel();
  thr eins = new thr(this);


  // Ende Variablen

  public main(final String title) {
    // Frame-Initialisierung
    super(title);
    addKeyListener(this);
    addWindowListener(new WindowAdapter() {
      public void windowClosing(final WindowEvent evt) { System.exit(0); } });
    final int frameWidth = 200;
    final int frameHeight = 200;
    setSize(frameWidth, frameHeight);
    setResizable(false);

    Container cp = getContentPane();  cp.setLayout( null );    //JFrame
    //setLayout( null );


    // Anfang Komponenten
    // Ende Komponenten

    setVisible(true);
  }


  public void keyPressed(KeyEvent event)
  {
  if (event.getKeyChar() == KeyEvent.CHAR_UNDEFINED)
    {
     bmario_ls = false;  //  Wenn Mario sich bewegt nicht mehr links oder recht stehen
      bmario_rs = false;
    int key = event.getKeyCode();
    //Funktionstaste abfragen
    if(key == KeyEvent.VK_RIGHT)                //Mario rechts laufen
      {

       if(mposx<140)
                    {
                     bmario_rj = false;
                     bmario_rw = true;
                     mposx=mposx+geschwindigkeit;
                    }
                  else
                    {
                     bmario_rj = false;
                     bmario_rw = true;
                     mposx=mposx;
                     gposx = gposx-geschwindigkeit;      //Objekte beim laufen verschieben
                     cposx = cposx-1;
                     bposx = bposx-geschwindigkeit;
                      if(bposx < vergleich)
                    {
                      mposx=mposx+geschwindigkeit;
                      bposx = bposx+geschwindigkeit;
                      gposx = gposx+geschwindigkeit;

                    }
                    }


       if(mposx>200)
                    {

                    }
      }
      else bmario_rw = false;

    if(key == KeyEvent.VK_LEFT)                // Mario links laufen
      {
                  if(mposx>2)
                    {
                     bmario_rj = false;
                     bmario_lw = true;

                     mposx=mposx-geschwindigkeit;
                    }
                  else
                    {
                     bmario_lw = true;
                     mposx=mposx;
                    }
      }
    else bmario_lw = false;

       if(key == KeyEvent.VK_UP)                //Mario hoch laufen
      {

       eins.jump();
      }

      if(key == KeyEvent.VK_DOWN)                //Mario runter laufen
      {
      }

    repaint();
    }

  }

  public void keyTyped(KeyEvent event)
  {


  }

  public void keyReleased(KeyEvent event)                   // Key losgelassen wieder stehen?!?!?
  {

      bmario_rw = false;
      bmario_lw = false;
      int key = event.getKeyCode();
       if(key == KeyEvent.VK_RIGHT)                //Mario rechts laufen
      {
      bmario_rs = true;
      }

       if(key == KeyEvent.VK_LEFT)                //Mario links laufen
      {
      bmario_ls = true;

      }
                                                           // Bis hier
  }
  // Anfang Ereignisprozeduren


  public void paint(Graphics g)
       {

          Image mario_lj;
          Image mario_rs;
          Image mario_rw;
          Image mario_ls;
          Image mario_lw;
          Image mario_rj;
          Image goomba;
          Image background;
          //Image cloud;
          background = getToolkit().getImage("background.gif");
          mario_lj = getToolkit().getImage("MarioJumpingLeft.gif");
         mario_rs = getToolkit().getImage("MarioStandingRight.gif");
         mario_rw = getToolkit().getImage("MarioRight.gif");
         mario_ls = getToolkit().getImage("MarioStandingLeft.gif");
         mario_rj = getToolkit().getImage("MarioJumpingRight.gif");
         mario_lw = getToolkit().getImage("MarioLeft.gif");
         //cloud = getToolkit().getImage("cloud.jpg");
         goomba = getToolkit().getImage("goomba.gif");
          g.drawImage(background,bposx,-7,this);

         //g.setColor(new Color(63,191,255));
         //g.fillRect(1,1,200,200);

         if (bmario_rw == true)
         {
          g.drawImage(mario_rw,mposx,mposy,this);
         }

           if (bmario_lw == true)
         {
          g.drawImage(mario_lw,mposx,mposy,this);
         }

            if (bmario_rj == true)
         {
          g.drawImage(mario_rj,mposx,mposy,this);
         }

           if (bmario_rs == true)
         {
          g.drawImage(mario_rs,mposx,mposy,this);
         }

           if (bmario_ls == true)
         {
          g.drawImage(mario_ls,mposx,mposy,this);
         }



         g.drawImage(goomba,gposx,169,this);                          //Goomba
          //g.drawImage(cloud,cposx,50,this);                            //Wolke



         //g.setColor(new Color(139,90,43));
         //g.fillRect(1,185,200,11);

       }


  // Ende Ereignisprozeduren

  public static void main(final String[] args) {
    new main("main");
  }
}[/HIGHLIGHT]


----------



## MiDniGG (14. Mrz 2009)

1.) Klassennamen groß! 
2.) Seh ich jetzt auf Anhieb nix von wegen Thread(...).start();

Und Tschüss


----------



## hdi (14. Mrz 2009)

Versuch das:

[HIGHLIGHT="Java"]public static void main(final String[] args) {
   SwingUtilities.invokeLater(new Runnable(){
        public void run(){
               new main("main");
        } 
    }
}[/HIGHLIGHT]

Sollte eigentlich funktionieren. Die Swing-Komponenten deines Programms 
laufen auf dem sog. Event Dispatch Thread, das ist ein eigener Thread den Java
voll-automatisiert steuert.

Dieser EDT verträgt sich eher schlecht mit anderen Threads, d.h. aus anderen Threads
heraus kann es schwierig werden, Komponenten aus dem EDT zu beeinflussen.
Oder so, ist bestimmt nicht optimal ausgedrückt aber die Thematik ist recht heftig.

Auf jeden Fall legst du mit obigem Code dein komplettes Programm auf den EDT,
und jetzt gibt es keinen anderen Thread mehr ausser dem EDT. Dadurch auch
keine Probleme mehr. (Hoffe ich)


----------



## Wildcard (15. Mrz 2009)

> Dieser EDT verträgt sich eher schlecht mit anderen Threads, d.h. aus anderen Threads
> heraus kann es schwierig werden, Komponenten aus dem EDT zu beeinflussen.
> Oder so, ist bestimmt nicht optimal ausgedrückt aber die Thematik ist recht heftig.


Nein. Andere Threads dürfen die Oberfläche nicht beeinflussen, ganz einfach Kiste. Wo es nötig ist, das ein Background Thread Einfluss auf die GUI hat, synchronisiert man mit dem EDT. Also zum Beispiel ein tableModel#addRow ruft man dann mit SwingUtilities#invokeLater auf. Somit wird der kleine Teil des Background Threads der die Oberfläche beeinflusst (und nur dieser) nicht im Background Thread selbst ausgeführt, sondern vom EDT.


> Auf jeden Fall legst du mit obigem Code dein komplettes Programm auf den EDT,
> und jetzt gibt es keinen anderen Thread mehr ausser dem EDT. Dadurch auch
> keine Probleme mehr. (Hoffe ich)


Nein. Eine Long Running Operation (sleep, Datenbankabfrage, Dateien auslesen/schreiben, große Berechnungen,...) *dürfen* nicht im EDT ausgeführt werden, da so lange die gesamte grafische Oberfläche blockieren würde.
Diese Teile müssen in einen seperaten Thread der bei Bedarf und wie oben beschrieben in die GUI 'zurückruft' mit invokeLater


----------



## hdi (15. Mrz 2009)

> Eine Long Running Operation (sleep, Datenbankabfrage, Dateien auslesen/schreiben, große Berechnungen,...) dürfen nicht im EDT ausgeführt werden, da so lange die gesamte grafische Oberfläche blockieren würde.
> Diese Teile müssen in einen seperaten Thread der bei Bedarf und wie oben beschrieben in die GUI 'zurückruft' mit invokeLater



So wie ich das jetzt verstehe, widerspricht sich doch der letzte Satz mit dem, 
was darüber steht?
Ich glaub ich hab da n Problem mit dem Verständnis darüber, wie das ganze intern 
abläuft..

Also wenn ich gesagt habe, "du legst dein Programm auf den EDT", meinte ich eben, es per
invokeLater drauf zu setzen. 

Also, das meinst du doch auch oder?
Und wenn man direkt "alles" per invokeLater startet, so wie in meinem Bsp Code,
dann passt das doch, oder? Ist quasi unnötig viel drauf gelegt, weil das Programm
ja selber schon Swing enthält, aber funktionieren müsste es. 
Hab ich das richtig verstanden, oder nicht?

edit: ...und dass "nur noch einen Thread" nicht stimmt, macht auch Sinn wenn ich
nochmal drüber nachdenk. Man sagt ja new Runnable(). Aber ich dachte bisher halt,
die EDT "saugt" den Thread quasi ein, und übernimmt dann alle Operationen.


----------



## Wildcard (15. Mrz 2009)

Nein, ich glaube du verstehst die Sache nicht ganz. Unabhängig davon in welchem Thread du deine GUI startest, das ist ein anderes Thema, bist du am Schluss immer in der Situation das deine GUI im EDT läuft.
1.Kein anderer Thread darf die GUI manipulieren
2.Der EDT darf nicht blockiert werden. 
Das sind die Grundregeln. An bestimmten Fällen werden allerdings long running operations benötigt die eigentlich die GUI blockieren würden.
Beispiel dafür: Button klick startet fette Berechnung
Damit die GUI nun allerdings nicht blockiert, startet man beim Button klick nicht die Berechnung selbst, sondern einen Thread der die Berechnung ausführt.
Nun will dieser Thread seine Berechnungen an GUI zurück liefern, darf dies aber wegen Regel 1 nicht. Also verpackt der Thread die Anweisung die GUI zu aktualisieren (und nur diese) in ein neues Runnable und postet sie mit SwingUtilities#invokeLater in die Queue des EDT.
Der EDT arbeitet seine Queue ab und trifft irgendwann auf das übergebene Runnable und führt es aus. Damit wird der Code der die GUI aktualisieren soll wieder im richtigen Thread Context ausgeführt, alles hat seine Ordnung und nichts blockiert.

Bevor ich mir den Mund fusselig tippe lasse ich's den Ben erklären:
Professional Swing: Threading


----------



## hdi (15. Mrz 2009)

Okay, erstmal der Link geht bei mir nicht  also ich komm auf ne Seite, ein
Flash Plugin, aber nix passiert. Es ist unten ein Menü für Play und weiter/zurück.
Aber der Main-Content bleibt weiss. Ich hab eig. alles drauf und nix deaktiviert.
Muss man das erst lange laden lassen? Selbst nach 10sec tut sich nix (DSL6000)

Eine Sache versteh ich aber grad bei deinem Text nicht: Wann läuft etwas im EDT,
und wann nicht?
Du sagst zB kein Thread darf die EDT manipulieren. Auch wenn jetzt die Operation
in einer actionPerformed Methode nur sehr klein ist, und sie etwas einfaches berechnet (x++ und ein Label-Text updaten).
Das wäre doch auch eine Manipulation.

Anscheinend ist das aber an dieser Stelle kein "anderer" Thread, sondern die EDT
selbst. 

Wie weit geht das quasi? Also wenn ich jetz eine Klasse extends JFrame habe,
dann ist das janicht gleich alles im EDT oder. Sieht man ja daran dass es eben
Probleme gibt.

Also.. naja vllt sagst du mir einfach wie ich dieses Video/Slideshow oder was das ist,
bei mir zum Laufen bring? vllt klärt sich ja dort wirklich alles, ansonsten kann ich ja
noch immer bei dir nachfragen.

Danke!


----------



## Wildcard (15. Mrz 2009)

Ja, ist Flash. Warum es bei dir nicht geht, kann ich dir nicht sagen.


> Eine Sache versteh ich aber grad bei deinem Text nicht: Wann läuft etwas im EDT,
> und wann nicht?


Alles was mit der AWT/Swing UI zu tun hat läuft in diesem Thread.
Dort wird das implizit erledigt, in anderen Toolkits wie SWT ist das ein expliziter Vorgang.


> Du sagst zB kein Thread darf die EDT manipulieren. Auch wenn jetzt die Operation
> in einer actionPerformed Methode nur sehr klein ist, und sie etwas einfaches berechnet (x++ und ein Label-Text updaten).
> Das wäre doch auch eine Manipulation.


Und wie wird die actionPerformed aufgerufen? Der Event Dispatcher verarbeitet das Mouse Event, sagt dem Button bescheid, der Button informiert die Listener (so grob). Ergo: dort läuft der Code im EDT.



> Anscheinend ist das aber an dieser Stelle kein "anderer" Thread, sondern die EDT
> selbst.


jup



> Wie weit geht das quasi? Also wenn ich jetz eine Klasse extends JFrame habe,
> dann ist das janicht gleich alles im EDT oder. Sieht man ja daran dass es eben
> Probleme gibt.


Klassen liegen nicht in Threads. In Threads werden Anweisungen ausgeführt. Nichts hindert andere Threads daran Methoden auf einer extends JFrame Klasse aufzurufen. Es ist Sache des Entwicklers über die JavaDoc zu sagen, ob das erlaubt ist oder nicht (und wo erwünscht auch explizit zu prüfen und evtl. Exception werfen). Fakt ist aber: ruft ein anderer Thread eine Methode auf die die GUI manipuliert, ist das Ergebnis undefiniert und Abstürze und fehlerhafte Anzeigen können die Folge sein. In komplexen Systemen kann es schwierig werden die Ursache genau zu lokalisieren, weil der Fehler sich dann oft kausal nicht mehr mit dem asynchronen Aufruf korrellieren lässt.
SWT löst dieses Problem in meinen Augen besser. Jede Methode die die GUI manipuliert überprüft direkt den aufrufenden Thread und wirft sofort eine Exception wenn es nicht der UI Thread war.


----------



## hdi (15. Mrz 2009)

Bist du sicher, dass der Link i.O. ist?

Ich habs jetzt mit IE7, Opera und FF3 versucht.
Bei IE7 und Opera kommt nur ein grauer Bildschirm, bei FF3 kommt das
"loading" usw und ebne das Menü, aber nichts passiert...

Ich will das sehen ;(


----------



## SlaterB (15. Mrz 2009)

@hdi: ich sehe auch nur weiß

--------

zu diesem Thema zurück: nur weil eine Klasse von Thread erbt, ändert das nicht unbedingt das Verhalten wie gewünscht,

entweder sich zu Threads schlau machen oder genau das Konstrukt verwenden, welches angegeben wurde,
sonst wird jump() weiterhin vom Event-Thread ausgeführt -> GUI blockiert,


----------



## Wildcard (15. Mrz 2009)

hdi hat gesagt.:


> Bist du sicher, dass der Link i.O. ist?


Works for me


----------



## hdi (15. Mrz 2009)

Du machst doch nur wieder Werbung für Ubuntu mit irgendeinem komschen Linux-Link 

Das dumme ist ich hab auch lange danach gegooglet, aber scheinbar ist dieser Link
echt die einzigste Quelle für dieses Video. Ich würd das so gerne sehen 

ps: haste das vllt noch im browser cache?


----------



## Wildcard (15. Mrz 2009)

Die Expert Presentation Series von Java Lobby hat einige ganz ausgezeichnete Vorträge von Java Experten. Leider sind sie über die Hauptseite kaum noch auffindbar. Eine  Schande diese Vorträge nicht an prominenterer Stelle zugänglich zu machen.


----------



## hdi (15. Mrz 2009)

Ich hab die Rubrik gefunden, leider sind die unteren 50% der Links tot.
Darunter jegliche Videos über Swing und Threading ... 

So'n Quatsch  Also ich will ja nicht nerven, aber Wildcard ich glaub du musst
dir jetzt den Mund fusslig reden  Bitte..


----------



## Wildcard (15. Mrz 2009)

Was hast du denn noch nicht verstanden?


----------



## hdi (15. Mrz 2009)

Naja eben woher ich jetzt genau weiss wann ich wo irgendwas mit invokeLater
machen muss, und wann nicht.

Also alle GUI-Listener werden vom EDT ausgeführt, ok.
Aber im Konstruktor eines JFrames zB, wenn ich dort irgendwie den Text
von einem Label ändere, läuft das auch auf dem EDT?

Ich weiss auch nicht was der Unterschied zwischen EventQueue.invokeLater
und SwingUtilities.invokeLater ist.

Es ist bei mir einfach so rumgerate... Und das is halt doof


----------



## Wildcard (15. Mrz 2009)

hdi hat gesagt.:


> Naja eben woher ich jetzt genau weiss wann ich wo irgendwas mit invokeLater
> machen muss, und wann nicht.


Wenn ein nicht-UI Thread die GUI ändert


> Also alle GUI-Listener werden vom EDT ausgeführt, ok.
> Aber im Konstruktor eines JFrames zB, wenn ich dort irgendwie den Text
> von einem Label ändere, läuft das auch auf dem EDT?


Hängt davon ab welcher Thread das Objekt instanziert, JLabel#setText ist allerdings sowieso Threadsafe (siehe JavaDoc).



> Ich weiss auch nicht was der Unterschied zwischen EventQueue.invokeLater
> und SwingUtilities.invokeLater ist.


Gibt keinen, same thing


----------



## Tossel (2. Apr 2009)

hm na ja mein problem besteht weiterhin  hab mir aber in der zwischenzeit mit einem animierten gif geholfen xD wirklich keiner eine lösung dafür?


----------



## Ebenius (6. Apr 2009)

Kannst Du Dein Problem nochmal zusammengefasst beschreiben; ich sehe in dem Thema nicht mehr durch.

Ebenius


----------

