Fighter: Neuen Thread Beispiel: Stoppuhr

Guten Tag zusammen,

ich bin neu in der Java Programmierung und möchte gerne das Thema Threads lernen. Bin dabei ein Programm zu schreiben welches eine Uhr/Stoppuhr braucht. Kann mir jemand vielleicht ein kleines Beispiel geben wie das mit einem neuen Thread abläuft. Wie man den Thread erstellt weiß ich schon (Thread uhr = new Thread();) zb... Aber wie verknüpfe ich jetzt die Uhr damit. public void start() und stop() mit der Zeit habe ich auch schon (System.currentMillis ..... usw.) soweit fertig. Die Zeit läuft bei mir schon. Nur "live" kann ich sie nicht ausgeben. Nur wenn ich start und dann stop() aufrufe kann ich die zeit ablesen. Dafür brauche ich den neuen Thread.

Kann mir jemand helfen?

Gruß

  1. Wozu brauchst du dazu einen Thread? Für eine Stoppuhr würde ich bei Start und Stop die aktuelle Zeit nehmen und voneinander abziehen.
    Für eine Anzeige während der Laufzeit ist ein Timer geeignet, der in einem bestimmten Zeitintervall die aktuelle Zeit berechnet und anzeigt.
    Wo siehst du da einen sinnvollen Einsatz eines Threads?

    Du kannst natürlich schon irgendwas in einen Thread auslagern um Threads kennenzulernen. Aber da würde ich mir ein besseres Beispiel suchen.

    Übrigens deine Beschreibung ist nicht so wirklich verständlich, daraus sehe ich nicht was du genau vor hast.

    1. Wozu brauchst du dazu einen Thread? Für eine Stoppuhr würde ich bei Start und Stop die aktuelle Zeit nehmen und voneinander abziehen.
      Für eine Anzeige während der Laufzeit ist ein Timer geeignet, der in einem bestimmten Zeitintervall die aktuelle Zeit berechnet und anzeigt.
      Wo siehst du da einen sinnvollen Einsatz eines Threads?

      Wie funktioniert das mit einem Timer?? Ich möchte eine Art "zeitlinie" machen. Dh. Die Linie läuft von links nach rechts mit der zeit (zb. 100 px pro Sek.) aber wenn ich das mit dem abziehen mache habe ich ja nicht den genauen Wert jede ms oder nicht?
      Die Linie soll so laufen wie die Zeitleiste bei einem Lied in iTunes zb.
      Wenn ich ich das mit Start stop mache und die Zeiten voneinander abziehe bewegt sich die Linie erst wenn ich wieder auf stop klicke.

      Du kannst natürlich schon irgendwas in einen Thread auslagern um Threads kennenzulernen. Aber da würde ich mir ein besseres Beispiel suchen.

      Was für ein Beispiel empfiehlst du mir da?

      Übrigens deine Beschreibung ist nicht so wirklich verständlich, daraus sehe ich nicht was du genau vor hast.

      Ja Sorry, hab Probleme mich genau auszudrücken bei so Sachen!

      Gruß

      1. Hallo,

        Wie funktioniert das mit einem Timer?? Ich möchte eine Art "zeitlinie" machen. Dh. Die Linie läuft von links nach rechts mit der zeit (zb. 100 px pro Sek.) aber wenn ich das mit dem abziehen mache habe ich ja nicht den genauen Wert jede ms oder nicht?

        Wenn Du ständig die aktuelle Zeit abfragst, also in etwa so:

          
        while (true) {  
           long actTime = System.currentTimeMillis();  
           // Tue irgendwas, z.b. Linie zeichnen  
        }  
        
        

        so hast Du den Nachteil, dass Du sehr viel CPU-Zeit damit verbrätst, immer und immer wieder durch die Schleife zu laufen. Besser ist es, ein bisschen zu warten, z.b. so:

          
        while (true) {  
           long actTime = System.currentTimeMillis();  
           // Tue irgendwas, z.b. Linie zeichnen  
          
           try {  
               thisThread.sleep(200);  
           } catch (InterruptedException e){}  
          
        }  
        
        

        Jetzt macht Dein Programm nur noch alle 200ms was (in CPU-Zyklen eine halbe Ewigkeit, in der Zwischenzeit kann es von Betriebssystem "schlafen gelegt" werden, und andere Programme können laufen).
        Es stimmt natürlich, Du hast damit (theoretisch) eine Art "Ruckeln" (= deine Linie wird nicht ständig aktualisiert, sondern eben nur alle 200ms) - bei ein paar 100ms ist das aber so wenig, dass es kaum ins Gewicht fallen dürfte - musst mal ein bisschen Rumspielen damit.

        Eine andere Möglichkeit (und das ist glaube ich auch die, auf die Encoder anspielt), ist der Einsatz der Timer-Klasse, mit der kannst Du bestimmte Dinge zu einem bestimmten Zeitpunkt ausführen, oder zeitverzögert oder...
        Aber wenn Du erstmal Threads kennenlernen willst, ist es ungünstig mit der Timer-Klasse anzufangen, denn die "Tasks", die Du damit steuerst, sind letztendlich widerrum Threads (somit solltest Du das Prinzip erstmal verinnerlicht haben).

        Verfolgen wir also Deine ursprüngliche Idee weiter:
        Du brauchst einen Thread, der die Uhrzeitanzeige übernimmt (Uhrzeit auslesen / Linie zeichnen /...). Hierzu musst Du für den Thread eine eigene Klasse schreiben, die das Interface "Runnable" implementiert. Damit dieser auch irgendwann wieder aufhört zu laufen, bekommt er einen "Stopp-Knopf" (siehe Methode "stopMe")

          
         class VisualizeTimeRunnable implements Runnable {  
                 private boolean hasStopped = false;  
          
                 // Konstruktor - Evtl. mit Parametern erweitern, die der Thread zum Ablaufen  
                 // benötigt  
                 public VisualizeTimeRunnable() {  
          
                 }  
          
                 public stopMe () {  
                     this.hasStopped = true;  
                 }  
          
                 public void run() {  
                     while (!hasStopped) {  
                        long actTime = System.currentTimeMillis();  
                         // Tue irgendwas, z.b. Linie zeichnen  
                         try {  
                            thisThread.sleep(200);  
                         } catch (InterruptedException e){}  
          
                     }  
                 }  
        }  
        
        

        Dieser Thread kann jetzt von Deinem Hauptprogramm gestartet und selbstverständlich auch wieder gestoppt werden. Der Einfachheit halber lassen wir den Thread einfach mal starten, 5 Sekunden laufen und dann stoppen:

          
        public static void main() {  
            VisualizeTimeRunnable clock = new VisualizeTimeRunnable();  
            Thread clockThread = new Thread(clock).start();  
            Thread.sleep(5000);  
            clock.stopMe();  
        }  
        
        

        Et voila! (Code nicht getestet, ohne Gewähr ;) ).

        Du kannst natürlich schon irgendwas in einen Thread auslagern um Threads kennenzulernen. Aber da würde ich mir ein besseres Beispiel suchen.
        Was für ein Beispiel empfiehlst du mir da?

        Dein Beispiel kann man theoretisch auch in einem einzigen Thread abfrühstücken, ich finde es aber gar nicht so schlecht gewählt, um sich mal mit Multi-Threading zu befassen:
        Ein Thread macht den ganzen Grafik/Anzeige-Schlonz, ein weiterer (also Dein Hauptprogramm) reagiert auf Benutzereingaben (in Deinem Fall "Start/stopp") - große GUIs machens de facto genauso.

        Viel Erfolg beim ausprobieren.

        Viele Grüße,
        Jörg

        1. Erstmal vielen Dank für deine ausführliche Antwort!
          Ich habe das jetzt mal versucht so wie du es gesagt hast mit dem 200ms sleep.

            
          while (true)  
          {   long actTime = System.currentTimeMillis();   // Tue irgendwas, z.b. Linie zeichnen  
           try {  
          thisThread.sleep(200);   }  
          catch (InterruptedException e){}}  
          
          

          Ich bekomme jetzt aber nur noch ein schwarzes Bild und das Programm hängt sich auf!
          Vielleicht schreib ich mal ein bisschen von meinem Code. Noch eine erklärung:
          Ich habe jetzt die Oberfläche in einer Hauptklasse mit dem Start und Stop Button. Nun habe ich eine 2. Klasse (extends JPanel), die dann in meinem JPanel, welches ich erstellt habe in der Hauptklasse die "Grafik" zeichnet. Dh. X-Zeitachse und die Linie die sich bewegen soll.
          Zuerst habe ich mal getestet ob sich die Linie 1px weiterbewegt wenn ich auf den Start button klicke -> hat funktioniert.
          Und dann habe ich halt angefangen mit der Uhr. Sie hat aber nur funktioniert wenn ich eben start geklickt habe, etwas gewartet habe und dann auf stop. Dann ist die Linie vom Ausgangspunkt auf den aktuellen Wert gesprungen. (Also nicht schön "Gewandert")

            
          	 private void Uhrstart(){  
          		    aktiviert=true;  
          		    millisekunden=System.currentTimeMillis() - millisekunden;  
          		  }  
          		  private void Uhrstop(){  
          		    if(aktiviert){  
          		      millisekunden=System.currentTimeMillis()-millisekunden;  
          		      aktiviert=false;  
          		    }  
          
          

          Dann habe ich in Uhrstart() try Thread.sleep(200) .... miteingefügt. Da hängt sich dann das Programm auf.
          Falls ich einen neuen Thread erstelle: Soll ich dann einen Thread für die Uhr verwenden, und die Grafik + Hauptklasse einen zusammen?

          Gruß

          1. Hallo nocheinmal !

            Habe jetzt eine neue Klasse geschrieben die Runnalbe ist. hier ist sie:

              
              
            public class Time implements Runnable{  
              
            	  
            	long millis;  
            	boolean lauft;  
            	@Override  
            	public void run() {  
            		lauft = true;  
            		millis = System.currentTimeMillis();  
            		while (true) System.out.println(millis);  
            		  
            	}  
              
            }  
            
            

            Nun habe ich in meinem Hauptprogramm die Klasse als neuen Thread erstellt.

              
            		final Time t = new Time();  
            		final Thread timethread = new Thread(t);  
            
            

            Das Final musste sein da ich sonst eine Fehlermeldung erhalte!
            Dann habe ich den Thread gestartet mit meinem Button:

              
            		JButton btnStart = new JButton("Start");  
            		btnStart.addActionListener(new ActionListener() {  
            			public void actionPerformed(ActionEvent arg0) {  
            				timethread.start();  
            
            

            Jetzt erhalte ich in der Konsole immer die Werte der long variable "millis".
            Nur sind diese komplett Falsch und immer gleich!!!
            1371378272509
            1371378272509
            1371378272509
            1371378272509

            Warum ist das so= Bzw. was mache ich Falsch??

            Ich habe noch eine 2 Sache ausprobiert mit einem Code für eine Stoppuhr den ich online gefunden habe.

              
            class Zeitklasse extends Thread {  
            	  
            	  
            	private boolean hasStopped = false;  
            	public boolean zeitlauft;  
            	public int HS, sek, min, h;  
            	  
            	 public Zeitklasse() {  
            		  
            		 }  
              
            	public  void stopMe() {  
            		 this.hasStopped = true;  
            		 zeitlauft = false ;  
            		 }  
            	  
            	 public void run() {  
            		 while (!hasStopped) {  
            			 zeitlauft = true;  
            			 try{  
            				 Thread.sleep(9);  
            			 }  
            			 catch(Exception e){}  
            			 if(HS <= 99){  
            		          HS++;  
            		        } else  {  
            		          HS = 0;  
            		          if(sek <= 59){  
            		            sek++;  
            		          }else {  
            		            sek = 0;  
            		            if(min <= 59){  
            		              min++;  
            		            } else {  
            		              min = 0;  
            		              h++;  
            		            }  
            		          }  
            		        }  
              
            	 }  
            	 }	  
            }  
            
            

            Diese Klasse zählt korrekt die sek nach oben! Ich erhalte auch wenn ich die sek in der Konsole ausgeben lasse den richtigen Wert. Nur wenn ich diesen wert dann in die Zeichenklasse einfüge und den sek Wert als X-Wert der Linie einfüge, dann bewegt sie Linie trotzdem nicht! Fehlt da das repaint() ? und wenn ja , wie kann man andauend repaint() aufrufen?

            Gruß

            1. Hallo,
              Erstmal zu Deinem ersten Code-Schnippsel:

              Jetzt erhalte ich in der Konsole immer die Werte der long variable "millis".
              Nur sind diese komplett Falsch und immer gleich!!!
              1371378272509
              1371378272509
              1371378272509
              1371378272509

              Das liegt daran, dass Du in der While-Schleife (in der Klasse "Time", Methode "run") nur die Ausgabe (system.out.println...) stehen hast. Die Ermittlung der aktuellen System-Zeit wird aber nur ein einziges mal durchgeführt.
              Wenn Du die run-Methode so anpasst:

                
                 @Override  
                 public void run() {  
                       lauft = true;  
                
                       while (true) {  
                           millis = System.currentTimeMillis();  
                           System.out.println(millis);  
                       }  
                 }  
              
              

              solltest Du immer die aktuelle Zeit in die Ausgabe bekommen.

              Nur wenn ich diesen wert dann in die Zeichenklasse einfüge und den sek Wert als X-Wert der >Linie einfüge, dann bewegt sie Linie trotzdem nicht! Fehlt da das repaint() ? und wenn ja , >wie kann man andauend repaint() aufrufen?

              Ich muss gestehen, ich habe schon länger nichts mehr mit jPanel & Co gemacht, weiss deswegen nicht genau, wie das da mit repaint usw funktioniert (also wann man das aufrufen muss).
              Normalerweise aber würde ich denken, Du musst das repaint einfach innerhalb der While-Schleife machen:

                
              public void run() {  
                 while (!hasStopped) {  
                 ... (Zeitberechnung usw)...  
                
                 // Repaint  
                 deinPanel.updateUI();  
                
                 }  
              }  
              
              

              Das setzt aber natürlich voraus, dass Deine Zeitklasse die Instanz deines jPanels (hier "deinPanel") kennt. Eine andere Möglichkeit wäre, die Zeitklasse direkt von jPanel erben zu lassen:

                
              public Zeitklasse extends JPanel implements Runnable {...}  
              
              

              dann ist die Zeitklasse selbst Dein jPanel, und du kannst direkt  "this.updateUI();" aufrufen.

              In dem Beispielcode werden allerdings die Sekunden immer wieder auf 0 zurück gesetzt:

                
              if(sek <= 59){  
                 sek++;  
              }else {  
                sek = 0  
                ...  
              
              

              das musst Du evtl. berücksichtigen, wenn Du eine kontinuierliche Linie zeichnen willst.

              Viele Grüße,
              Jörg

              1. Hallo,

                habe mal die Zeitklasse mit extends Grafik (meine Grafikklasse) implmements Runnable aktualisiert.

                Dann habe ich unter sek++ meine x variable in der Grafikklasse (int xwert) auch dazugeschrieben (also xwert++). dann habe ich es in der Konsole ausgeben lassen. Nun zählt der xwert hoch. Jede Sekunde 1 mehr. Also passt das! Jetzt fehlt nur noch das die Anzeige aktualisiert wird! Wenn ich nämlich Start drücke zählt der xwert schön nach oben, aber die Linie in meiner Grafik bewegt sich nicht vom Fleck!

                Gruß

                1. Es läuft!!!!!

                  Ich habe alles der Zeitklasse in die Grafikklasse eingefügt und mit Runnable erweitert.
                  Dann habe ich die Linie mit g.drawLine(10+sek, 150, 10+sek, 150); gezeichnet. Dann habe ich unter sek++ die repaint() Methode aufgerufen.
                  Nun läuft die Linie schön von Links nach rechts!!!

                  Vielen Dank für deine ausführliche Hilfe! Ich verstehe jetzt das Thema Threads auch wesentlich mehr!

                  Gruß
                  Fighter

      2. Ich möchte eine Art "zeitlinie" machen. Dh. Die Linie läuft von links nach rechts mit der zeit (zb. 100 px pro Sek.) aber wenn ich das mit dem abziehen mache habe ich ja nicht den genauen Wert jede ms oder nicht?

        Du hast nicht jede ms, das stimmt. Aber du willst deine Anzeige nicht 1000 mal pro Sekunde aktualisieren, das ist viel zu häufig und wird vom Menschen nicht flüssiger wahrgenommen als wenn du es nur z.B. alle 50 ms machst.
        Den Wert zum jeweiligen Zeitpunkt ist aber schon genau.

        Wenn ich ich das mit Start stop mache und die Zeiten voneinander abziehe bewegt sich die Linie erst wenn ich wieder auf stop klicke.

        Daher der Timer, damit auch vor dem Stop schon was passiert.

        Ich finde das Beispiel nicht so geeignet weil du hier nicht offensichtlich parallele Aktionen hast. Die Zeit läuft von selbst in der Uhr des Rechners und wird da nur immer wieder abgefragt. Start/Stop und die Anzeige zwischendurch können auch gut ohne Thread laufen.
        Besser wäre eine wirklich lange andauernde Aktion, die ablaufen soll ohne dass das Hauptprogramm dabei stehen bleibt.
        Such doch mal nach "Java thread example", vielleicht ist da was brauchbares zu finden.

        Zum Zugriff auf die Anzeige der Zeit vom Thread aus:
        Ich weiß nicht wie das in Java genau aussieht, in anderen Sprachen kannst du von einem Thread aus nicht einfach auf die Oberfläche zugreifen und was anzeigen. Falls das in Java auch so ist, handelst du dir damit andere Probleme ein die du für ein Lernprojekt nicht unbedingt auch noch brauchen kannst.