Wenn wir während unseres Programmierungsprozesses einige einfache Zeitaufgaben ausführen müssen, müssen wir keine komplexe Kontrolle durchführen. Wir können in Betracht ziehen, Timer -Timing -Aufgaben in JDK zu verwenden, um dies zu erreichen. Die folgende LZ analysiert den Java -Timer -Timer basierend auf seinem Prinzip, Beispiel und Timerdefekt.
1. Einführung
In Java muss eine vollständige Timing -Aufgabe vom Timer- und TimerTask -Klassen erledigt werden. So werden sie in der API definiert. Timer: Ein Werkzeug, mit dem Aufgaben anordnen, die Threads später in Hintergrund -Threads ausführen. Aufgaben können einmal ausgeführt oder wiederholt ausgeführt werden. Aufgabe von Timertask: Timer als Aufgabe geplant, die ausgeführt oder wiederholt wird. Wir können verstehen, dass Timer ein Timer -Tool ist, mit dem bestimmte Aufgaben in einem Hintergrund -Thread ausgeführt werden sollen, und TimerTask eine abstrakte Klasse, deren Unterklasse eine Aufgabe darstellt, die von Timer geplant werden kann.
Die Timer -Klasse bietet vier Konstruktormethoden im Werkzeugklassen -Timer. Jeder Konstruktor startet einen Timer -Thread. Gleichzeitig kann die Timer-Klasse sicherstellen, dass mehrere Threads ein einzelnes Timer-Objekt ohne externe Synchronisation freigeben können, sodass die Timer-Klasse Thread-Safe ist. Da jedoch jedes Timer -Objekt einem einzelnen Hintergrund -Thread entspricht, mit dem alle Timeraufgaben nacheinander ausgeführt werden, sollte die Zeit für die Ausführung unserer Thread -Aufgabe im Allgemeinen sehr kurz sein. Aufgrund besonderer Umstände ist die Ausführungszeit einer bestimmten Timer -Aufgabe jedoch zu lang, sodass sie den Timer -Task -Ausführungs -Thread "ausschließlich" und alle nachfolgenden Threads warten müssen, bis er ausgeführt wird, was die Ausführung nachfolgender Aufgaben verzögert und diese Aufgaben zusammenhüpfen lassen. Wir werden die spezifische Situation später analysieren.
Wenn das Programm den Timer initialisiert, wird die Timing -Aufgabe entsprechend der festgelegten Zeit ausgeführt. Timer bietet die Zeitplanmethode mit mehreren Überladungen zur Anpassung an verschiedene Situationen wie folgt:
Zeitplan (TimerTask -Aufgabe, Datumszeit): Planen Sie die Ausführung der angegebenen Aufgabe zum angegebenen Zeitpunkt.
Zeitplan (TimerTask -Aufgabe, Datum zum ersten Mal, langer Zeitraum): Planen Sie die angegebene Aufgabe, um die wiederholte Ausführung der festen Verzögerung zum angegebenen Zeitpunkt zu starten.
Zeitplan (TimerTask -Aufgabe, lange Verzögerung): Planen Sie die angegebene Aufgabe, die nach der angegebenen Verzögerung ausgeführt werden soll.
Zeitplan (TimerTask -Aufgabe, lange Verzögerung, langer Zeitraum): Planen Sie die angegebene Aufgabe, die nach der angegebenen Verzögerung wiederholt verzögert wird.
Gleichzeitig wird die ScheduleatFixedRate -Methode ebenfalls überladen. Die ScheduleatFixedrat -Methode entspricht dem Zeitplan, aber ihr Fokus ist unterschiedlich und der Unterschied wird später analysiert.
ScheduleatFixedRate (TimerTask -Aufgabe, Datum des ersten Mals, langer Zeitraum): Planen Sie die angegebene Aufgabe, die zu einem bestimmten Zeitpunkt wiederholt zu einem festgelegten Preis ausgeführt werden soll.
ScheduleatFixedRate (TimerTask-Aufgabe, lange Verzögerung, langer Zeitraum): Planen Sie die angegebene Aufgabe, um nach der angegebenen Verzögerung wiederholte Ausführung mit fester Rate zu starten.
TIMERTask
Die TimerTask -Klasse ist eine abstrakte Klasse, die von Timer als Aufgabe arrangiert oder wiederholt wird. Es verfügt über eine abstrakte Methode Run () -Methode, mit der die von der entsprechenden Timer -Aufgabe ausgeführten Operationen durchgeführt werden sollen. Daher muss jede bestimmte Aufgabenklasse Timertask erben und dann die Run () -Methode überschreiben.
Darüber hinaus verfügt es über zwei nicht abstrakte Methoden:
Boolean Cancel (): Abbrechen Sie diese Timer -Aufgabe.
Long plhoedexecutiontime (): Gibt die geplante Ausführungszeit der neuesten tatsächlichen Ausführung dieser Aufgabe zurück.
2. Beispiele
2.1. Geben Sie die Verzögerungszeit für die Ausführung von Zeitaufgaben an
public class timertest01 {timer timer; public timertest01 (int time) {timer = new Timer (); Timer.Schedule (neuer Timertaskest01 (), Zeit * 1000); } public static void main (String [] args) {System.out.println ("Timer begin ..."); neuer TICEERTEST01 (3); }} public class timertaskest01 erweitert Timertask {public void run () {System.out.println ("Zeit ist up !!!"); }}Auslaufergebnisse:
Erster Druck:
Timer beginnt ....
Drucken in 3 Sekunden:
Die Zeit ist hoch !!!
2.2. Führen Sie die Zeitaufgaben zum bestimmten Zeitpunkt aus
public class timertest02 {timer timer; public timertest02 () {Datum Uhrzeit = Gettime (); System.out.println ("Zeitpunkt Zeitpunkt" + Zeit); Timer = neuer Timer (); Timer.Schedule (neuer Timertaskest02 (), Zeit); } public date GetTime () {Calendar calendar = calendar.getInstance (); Calendar.set (Calendar.hour_of_day, 11); Calendar.set (Calendar.minute, 39); Calendar.set (Calendar.second, 00); Datum Uhrzeit = calendar.getTime (); Rücklaufzeit; } public static void main (String [] args) {new timertest02 (); }} public class timertaskest02 erweitert Timertask {@Override public void run () {System.out.println ("Threadaufgaben zur bestimmten Zeit ausführen ..."); }}Wenn die Zeit um 11:39:00 Uhr erreicht ist, wird die Thread -Aufgabe natürlich ausgeführt, sie wird ausgeführt, auch wenn sie größer ist als in dieser Zeit! ! Das Ausführungsergebnis ist:
Angegebene Zeitzeit = Di 10. Juni 11:39:00 CST 2014 angegebene Zeit zum Ausführen von Threadaufgaben ...
2.3. Nach der Verzögerung der angegebenen Zeit wird die Zeitaufgabe in der angegebenen Intervallzeit ausgeführt.
public class timertest03 {timer timer; public timertest03 () {timer = new Timer (); Timer.Schedule (neuer Timertaskest03 (), 1000, 2000); } public static void main (String [] args) {new timertest03 (); }} public class timertaskest03 erweitert TimerTask {@Override public void run () {Datum date = neues Datum (this.ScheduledexecutionTime ()); System.out.println ("Die Zeit für die Ausführung dieses Threads ist:" + Datum); }}Auslaufergebnisse:
Die Zeit für die Ausführung dieses Threads ist: Di Jun 10 21:19:47 CST 2014 Die Zeit für die Ausführung dieses Threads ist: Di Jun 10 21:19:49 CST 2014 Die Zeit für die Ausführung dieses Threads ist: TUE 10. Juni 21:19:51 CST 2014 Die Zeit für die Ausführung dieses Threads. 21:19:55 CST 2014 Die Zeit für die Ausführung dieses Threads ist: aus 10. Juni 21.19 .19:57 CST 2014 ...................................................................................................................................
Für diese Thread -Aufgabe wird sie die Aufgabe nicht beenden, es wird weiter ausgeführt.
Für die oben genannten Beispiele hat LZ es nur kurz demonstriert und das Beispiel der ScheduleatFixedrate -Methode nicht erklärt. Tatsächlich entspricht diese Methode mit der Zeitplanmethode!
2.4. Analysieren Sie den Zeitplan und ScheduleatFixedrate
(1) Zeitplan (TimerTask -Aufgabe, Datumszeit), Zeitplan (TimerTask -Aufgabe, lange Verzögerung)
Bei beiden Methoden wird die Aufgabe sofort ausgeführt. Scheduledexecutiontime ändert sich aufgrund einer übermäßigen Ausführung einer Aufgabe nicht.
(2) Zeitplan (TimerTask -Aufgabe, Datum des ersten Zeitpunkts, langer Zeitraum), Zeitplan (TimerTask -Aufgabe, lange Verzögerung, langer Zeitraum)
Diese beiden Methoden unterscheiden sich etwas von den oben genannten zwei. Wie bereits erwähnt, wird die Timer -Timer -Aufgabe verzögert, da die vorherige Aufgabe lange ausgeführt wird. In diesen beiden Methoden ändert sich die geplante Zeit jeder ausgeführten Aufgabe mit der tatsächlichen Zeit der vorherigen Aufgabe, dh enderExecutiontime (n+1) = Reexecutiontime (n)+Periode. Das heißt, wenn die n -te Aufgabe diesen Ausführungszeitprozess aufgrund einer Situation und schließlich SystemcurrentTime> = teplaneDexecutiontime (N+1) verursacht, ist dies die N+1 -Aufgabe und wird aufgrund der Zeit nicht ausgeführt. Es wird darauf warten, dass die n -te Aufgabe vor der Ausführung ausgeführt wird, und dies führt zwangsläufig zur Veröffentlichung und Änderung der N -+2 -Ausführung implementiert, dh plantexecutiontime (n+2) = ReefePeaseCutiontime (N+1)+Zeitzeit. Daher achten diese beiden Methoden mehr auf die Stabilität der Speicherintervallzeit.
(3) ScheduleatFixedRate (TimerTask -Aufgabe, Datum am ersten Mal, langer Zeitraum), ScheduleatFixedRate (TimerTask -Aufgabe, lange Verzögerung, langer Zeitraum)
Wie bereits erwähnt, ist der Schwerpunkt von ScheduleatFixedrate- und Zeitplanmethoden unterschiedlich. Die Zeitplanmethode konzentriert sich auf die Stabilität der Sparenintervallzeit, während sich die ScheduleatFixedrate -Methode mehr auf die Aufrechterhaltung der Stabilität der Ausführungsfrequenz konzentriert. Warum sagst du das, die Gründe sind wie folgt. In der Zeitplanmethode führt die Verzögerung der vorherigen Aufgabe zu einer Verzögerung der Zeitverzögerung, während die ScheduleatFixedRate -Methode nicht der Fall ist. Wenn die Ausführungszeit der n -ten Aufgabe zu lang ist, wird das SystemcurrentTime> = ungedExecutiontime (N+1) nicht darauf warten, dass die N+1 -Aufgabe sofort ausgeführt wird. Daher unterscheidet sich die Berechnungsmethode der Ausführungszeit der ScheduleatFixedrat -Methode vom Zeitplan, jedoch plantexecutiontime (n) = firstExecUTetime +n*Periodenzeit, und die Berechnungsmethode bleibt für immer unverändert. Daher konzentriert sich ScheduleatFixedRate mehr darauf, die Ausführungsfrequenz stabil zu halten.
3. Timer -Defekte
3.1. Timerfehler
Der Timer -Timer kann zeitlich Zeit (Aufgaben zum bestimmten Zeitpunkt ausführen), Verzögerungsaufgaben (5 Sekunden) und regelmäßig Aufgaben ausführen (Aufgaben bei 1 Sekunde ausführen), aber der Timer hat einige Mängel. Erstens basiert die Unterstützung von Timer für die Planung auf der absoluten Zeit und nicht auf der relativen Zeit. Daher ist es sehr empfindlich gegenüber Änderungen der Systemzeit. Zweitens fängt der Timer -Thread keine Ausnahmen auf. Wenn die nicht überprüfte Ausnahme von Timertask ausgelöst wird, wird der Timer -Thread beendet. Gleichzeitig wird der Timer die Ausführung des Threads nicht fortsetzen und fälschlicherweise glauben, dass der gesamte Timer -Thread storniert wird. Gleichzeitig wird TimerTask, das für noch nicht ausgeführt wurde, nicht mehr ausgeführt, und es können nicht neue Aufgaben geplant werden. Wenn TimerTask eine ungeprüfte Ausnahme ausgelegt hat, erzeugt der Timer unvorhersehbares Verhalten.
(1) Timer -Verwaltungszeitverzögerungsfehler. Bevor der Timer bei der Ausführung einer zeitgesteuerten Aufgabe nur eine Thread -Aufgabe erstellt. Wenn es mehrere Threads gibt, wenn einer der Threads aus irgendeinem Grund zu lange zu lange ist und das Intervall zwischen den beiden Aufgaben zu lang ist, treten einige Defekte auf:
public class timertest04 {privater Timer -Timer; öffentlicher langer Start; public timertest04 () {this.Timer = new Timer (); start = system.currentTimemillis (); } public void timerOne(){ timer.schedule(new TimerTask() { public void run() { System.out.println("timerOne invoked ,the time:" + (System.currentTimeMillis() - start)); try { Thread.sleep(4000); //Thread sleeps 3000 } catch (InterruptedException e) { e.printStackTrace(); } }}}, 1000); } public void timerTwo () {timer.Schedule (new Timertask () {public void run () {System.out.println ("Timerone aufgerufen, die Zeit:" + (System.CurrentTimemillis () - Start);}}, 3000); } public static void main (String [] args) löst eine Ausnahme aus {timertest04 test = new timertest04 (); test.timerone (); test.timertwo (); }}Nach unserem normalen Denken sollte Timertwo nach 3s ausgeführt werden, und das Ergebnis sollte sein:
Timerone aufgerufen, die Zeit: 1001 Timerone aufgerufen, die Zeit: 3001
Aber die Dinge widersprachen meinen Erwartungen. Timerone schläft (4000), schläft 4S und der Timer befindet sich im Timer, wodurch die Zeit für TimeOne das Intervall überschreitet. Das Ergebnis:
Timerone aufgerufen, die Zeit: 1000 Timerone aufgerufen, die Zeit: 5000
(2) Timer wirft einen Ausnahmemangel aus. Wenn TimerTask eine RunTimeException auswirkt, beendet der Timer den Lauf aller Aufgaben. wie folgt:
public class timertest04 {privater Timer -Timer; public timertest04 () {this.Timer = new Timer (); } public void Timerone () {Timer.Schedule (neuer TimerTask () {public void run () {throw New RunTimeException ();}}, 1000); } public void timerTwo () {timer.Schedule (neuer Timertask () {public void run () {System.out.println ("werde ich es ausführen?");}}, 1000); } public static void main (String [] args) {timertest04 test = new timertest04 (); test.timerone (); test.timertwo (); }}Auslaufergebnis: Timerone löst eine Ausnahme aus und führt dazu, dass die TimerTwo -Aufgabe beendet wird.
Ausnahme in Thread "Timer-0" java.lang.runtimeexception bei com.chenssy.timer.timerTest04 $ 1.run (timerTest04.java:25) bei Java.util.TimerThread.Mainloop (Timer.java:555) in java.util.til.timerthead.run.run (timer.java:505).
Bei Timer -Mängel können wir geplante Threadpoolexecutor als Ersatz in Betracht ziehen. Timer basiert auf absoluter Zeit und reagiert empfindlicher für die Systemzeit, während geplant Threadpoolexecutor auf der relativen Zeit basiert. Timer ist intern ein einzelner Thread, während geplanter Threadpoolexecutor intern ein Thread -Pool ist, sodass er die gleichzeitige Ausführung mehrerer Aufgaben unterstützt.
3.2. Timer ersetzen
(1) Lösen Sie das Problem eins:
öffentliche Klasse ScheduledexecUTORTEST {private enderEDexecutorService afuexec; öffentlicher langer Start; EnderEdexecUTORTEST () {this.Scheduexec = Executors.NewScheduledThreadpool (2); this.start = system.currentTimemillis (); } public void Timerone () {enderUexec.schedule (new Runnable () {public void run () {System.out.println ("Timeron, die Zeit:" + (System.CurrentTimemillis () - Start); Try {Thread.Sleep (4000); }, 1000, TimeUnit.Millisekunden); } public void timerTwo () {planeLexexec.Schedule (new Runnable () {public void run () {System.out.println ("timerTwo, the the the the time:" + (System.currentTimemillis () - Start);}}, 2000, Timeunit.Milliseconds); } public static void main (String [] args) {teplanexecutportest test = new afuleDexecUTORTEST (); test.timerone (); test.timertwo (); }}Auslaufergebnisse:
Timerone, die Zeit: 1003 TICEERTWO, THE TIME: 2005
(2) das Problem zwei lösen
öffentliche Klasse ScheduledexecUTORTEST {private enderEDexecutorService afuexec; öffentlicher langer Start; EnderEdexecUTORTEST () {this.Scheduexec = Executors.NewScheduledThreadpool (2); this.start = system.currentTimemillis (); } public void Timerone () {enderUexec.schedule (new Runnable () {public void run () {neue runTimeException ();}}, 1000, TimeUnit.Milliseconds); } public void timerTwo () {planeLexexec.SchulenatFixedRate (new Runnable () {public void run () {System.out.println ("timerTwo aufgerufen .....");}}, 2000.500, timeunit.milliseconds); } public static void main (String [] args) {teplanexecutportest test = new afuleDexecUTORTEST (); test.timerone (); test.timertwo (); }}Auslaufergebnisse:
TIMERERTWO aufgerufen ... TIMERTWO aufgerufen ... TIMERTWO aufgerufen ... TIMERERTWO aufgerufen ... TIMERTWO aufgerufen ... TIMERTWO aufgerufen ... TIMERTWO Invooked ... TIMERERTWO Invooked ... TIMERERTWO aufgerufen ... TimerTwo aufgerufen ...
4. Verwenden Sie den Timer, um den Ball zu erreichen
Ein Beispiel im Simulationsbuch machte einen Flipper, der mehrere Kreise an der festgelegten Position auf der Leinwand zeichnen, und nach einer Zeit der Verzögerung wurde es in einer nahe gelegenen Position umgezeichnet. Lassen Sie den Ball zu bewegt und passen Sie die Verzögerung durch die JSPINNER -Komponente ein, um die Bewegungsgeschwindigkeit des Balls zu steuern.
Ballscanvas.java
öffentliche Klasse Ballscanvas erweitert Canvas implementiert ActionListener, FocusListener {private Ballbälle []; // Mehrere Bälle privater Timer -Timer; privater statischer Klassenball {int x, y; // Farbfarbe koordinieren; // Farbe boolean nach oben; // Bewegungsrichtung (int x, int y, Farbe Farbe) {this.x = x; this.y = y; this.color = color; up = links = false; }} public ballscanvas (Farbfarben [], int delay) {// Initialisieren Sie Farbe und Verzögerung dies.balls = neuer Ball [Farben.Length]; für (int i = 0, x = 40; i <farben.length; i ++, x+= 40) {kugeln [i] = neuer Ball (x, x, farben [i]); } this.addfocusListener (this); Timer = neuer Timer (Verzögerung, dies); // Erstellen Sie ein Timer -Objekt, Verzögerung Geben Sie den Verzögerungs -Timer an.start (); } // Delay Public void setDelay (int delay) {timer.setDelay (delay); } // public void Paint zeichnen (Grafik g) {für (int i = 0; i <kugslänge; i ++) {g.setColor (Bälle [i] .Color); // Farbkugeln setzen [i] .x = Bälle [i] .left? Bälle [i] .x - 10: Bälle [i] .x + 10; if (Bälle [i] .x <0 || Bälle [i] .x> = this.getWidth ()) {// Die Richtung in horizontale Bälle ändere [i] .left =! Bälle [i] .left; } Bälle [i] .y = Bälle [i] .Up? Bälle [i] .y - 10: Bälle [i] .y + 10; if (Bälle [i] .y <0 || Bälle [i] .y> = this.getheight ()) {// Ändern Sie die Richtung in die vertikalen Richtungsbälle [i] .Up =! Bälle [i] .Up; } G.filloval (Bälle [i] .x, Bälle [i] .y, 20, 20); // Zeichnen Sie einen Kreis des angegebenen Durchmessers}} // Timer Timed Execution Ereignis @Override public void ActionPersformed (actionEvent e) {repaint (); // Repaint} // Focus @Override public void focusgained (focusevent e) {timer.stop (); // Timer Stop} // Lost Focus @Override public void FocusLost (focusevent e) {timer.restart (); // Timer neu starten}}Ballsjframe.java
Klasse ballsjFrame erweitert JFrame implementiert Changelistener {privater Ballscanvas -Ball; Privat Jspinnner Spinner; public ballsjframe () {Super ("Pinball"); this.Setbounds (300, 200, 480, 360); this.setDefaultCloseOperation (exit_on_close); Farbfarben [] = {color.red, color.green, color.blue, color.magenta, color.cyan}; Ball = neue Ballscanvas (Farben, 100); this.getContentPane (). add (Ball); JPanel Panel = new Jpanel (); this.getContentPane (). Add (Panel, "South"); panel.Add (New Jlabel ("Delay")); Spinner = new Jspinnner (); Spinner.SetValue (100); panel.Add (Spinner); Spinner.Addchangelistener (dies); this.setvisible (true); } @Override public void staitechanged (Changeevent e) {// Bei der Änderung des JSpinnner -Werts, klicken Sie auf die Schaltfläche "Auf oder Ab des Abwärts" von JSPINNER oder drücken Sie in jspinner ball.setDelay (Integer.ParseInt (" + Spinner.getValue ()); } public static void main (String [] args) {new ballsjframe (); }} Die Effekte sind wie folgt: