Si nous devons exécuter des tâches de synchronisation simples au cours de notre processus de programmation, nous n'avons pas besoin de contrôler un contrôle complexe. Nous pouvons envisager d'utiliser les tâches de synchronisation de la minuterie dans JDK pour y parvenir. Le LZ suivant analyse le minuteur de la minuterie Java en fonction de son principe, de son exemple et de son défaut de temporisation.
1. Introduction
En Java, une tâche de synchronisation complète doit être effectuée par les classes de minuterie et de tricotasque. C'est ainsi qu'ils sont définis dans l'API. Timer: un outil qui l'utilise pour organiser des tâches que les threads effectuent dans des threads d'arrière-plan plus tard. Les tâches peuvent être exécutées une fois, ou elles peuvent être exécutées à plusieurs reprises. Tâche planifiée par Timemersk: Timer en tant que tâche exécutée ou répétée. Nous pouvons comprendre que Timer est un outil de minuterie utilisé pour planifier pour exécuter des tâches spécifiées dans un thread d'arrière-plan, et Timertask une classe abstraite dont la sous-classe représente une tâche qui peut être planifiée par Timer.
La classe de temporisation fournit quatre méthodes de constructeur dans la minuterie de la classe d'outils. Chaque constructeur démarre un fil de minuterie. Dans le même temps, la classe de temporisation peut garantir que plusieurs threads peuvent partager un seul objet de minuterie sans synchronisation externe, de sorte que la classe de minuterie est filée. Cependant, comme chaque objet Timer correspond à un seul thread d'arrière-plan, qui est utilisé pour exécuter toutes les tâches de minuterie en séquence, en général, le temps consacré à l'exécution de notre tâche de thread devrait être très court. Cependant, en raison de circonstances spéciales, le temps d'exécution d'une certaine tâche de temporisation est trop long, il sera donc "exclusivement" du thread d'exécution de la tâche de temporisation, et tous les fils suivants doivent attendre qu'il soit exécuté, ce qui retardera l'exécution des tâches suivantes et fera s'accumuler ces tâches ensemble. Nous analyserons la situation spécifique plus tard.
Lorsque le programme initialise la minuterie, la tâche de synchronisation sera exécutée en fonction du moment où nous avons défini. Timer fournit la méthode de planification, qui a plusieurs surcharges pour s'adapter à différentes situations, comme suit:
Calendrier (tâche Timemertass, date de date): Planifiez l'exécution de la tâche spécifiée à l'heure spécifiée.
Calendrier (tâche TIMERTASK, date de première fois, longue période): Planifiez la tâche spécifiée pour démarrer l'exécution de retard fixe répété à l'heure spécifiée.
Calendrier (tâche TIMERTASK, Long Delay): Planifie la tâche spécifiée à exécuter après le retard spécifié.
Calendrier (tâche Timertask, long retard, longue période): Planifiez la tâche spécifiée à exécuter à plusieurs reprises a fixé un retard après le retard spécifié.
En même temps, la méthode ScheduleAtFixeDrate est également surchargée. La méthode ScheduleAtFixeDrate est la même que le calendrier, mais leur objectif est différent, et la différence est analysée plus tard.
ScheduleAtFixeDrate (tâche Timertask, date de première fois, longue période): Planifiez la tâche spécifiée à exécuter à plusieurs reprises à un taux fixe à une heure spécifiée.
ScheduleAtFixeDrate (tâche Timertask, retard long, longue période): Planifiez la tâche spécifiée pour démarrer l'exécution répétée du taux fixe après le retard spécifié.
Timinerte
La classe TIMERTASK est une classe abstraite organisée par minuterie comme une tâche qui est exécutée ou répétée. Il a une méthode abstraite exécutée (), qui est utilisée pour effectuer les opérations à effectuer par la tâche de minuterie correspondante. Par conséquent, chaque classe de tâches spécifique doit hériter de Timertask, puis remplacer la méthode run ().
De plus, il a deux méthodes non abstraites:
booléen annule (): annuler cette tâche de minuterie.
Long ScheduleDexecutionTime (): renvoie le temps d'exécution planifié de l'exécution réelle la plus récente de cette tâche.
2. Exemples
2.1. Spécifiez le temps de retard pour exécuter les tâches de synchronisation
classe publique TimerTest01 {Timer Timer; public tinmerST01 (int time) {timer = new Timer (); Timer.Schedule (nouveau TimertAskTest01 (), temps * 1000); } public static void main (String [] args) {System.out.println ("Timer Begin ..."); New TinerTest01 (3); }} classe publique TimertAskTest01 étend Timertask {public void run () {System.out.println ("Time's Up !!!"); }}Résultats en cours:
Première impression:
la minuterie commence ...
Imprimer en 3 secondes:
Le temps est écoulé !!!
2.2. Exécuter les tâches de synchronisation à l'heure spécifiée
classe publique TimerTest02 {Timer Timer; public tiperSt02 () {date heure = getTime (); System.out.println ("Spécifiez Time =" + Time); Timer = new Timer (); Timer.Schedule (new TimertAskTest02 (), temps); } public Date getTime () {calendar calendar = calendar.getInstance (); calendar.set (calendar.hour_of_day, 11); calendar.set (calendar.minute, 39); calendar.set (calendar.second, 00); Date heure = calendar.getTime (); heure de retour; } public static void main (String [] args) {new TimeTest02 (); }} classe publique TimertAskTest02 étend Timertask {@Override public void run () {System.out.println ("Exécuter des tâches de threads à l'heure spécifiée ..."); }}Lorsque le temps atteint 11:39:00, la tâche de fil sera exécutée, bien sûr, elle sera exécutée même si elle est supérieure à ce moment! ! Le résultat de l'exécution est:
Temps spécifié = TUe 10 juin 11:39:00 CST 2014 Temps spécifié pour exécuter des tâches de thread ...
2.3. Après avoir retardé l'heure spécifiée, la tâche de synchronisation sera exécutée à l'intervalle spécifié.
classe publique TinerTest03 {Timer Timer; public tinmerST03 () {timer = new Timer (); Timer.Schedule (New TimertaSkTest03 (), 1000, 2000); } public static void main (String [] args) {new TimerST03 (); }} classe publique TimertAskTest03 étend Timertask {@Override public void run () {date date = new Date (this.ScheduleDeXecutionTime ()); System.out.println ("L'heure d'exécution de ce thread est:" + date); }}Résultats en cours:
Le moment pour l'exécution de ce fil est: mar 10 juin 21:19:47 CST 2014 Le moment de l'exécution de ce thread est: mar 10 juin 21:19:49 CST 2014 Le moment pour exécuter ce thread est: mar juin 10 21:19:51 2014 Le temps d'exécuter ce fil est: mar 10 juin 21:19:57 CST 2014 ...............
Pour cette tâche de thread, si nous n'arrêtons pas la tâche, elle continuera à s'exécuter.
Pour les trois exemples ci-dessus, LZ l'a brièvement démontré et n'a pas expliqué l'exemple de la méthode ScheduleAtFixeDrate. En fait, cette méthode est la même que la méthode du calendrier!
2.4. Analyser le calendrier et le scheduleatFixeDrate
(1) Planification (tâche Timemery, date de date), calendrier (tâche Timertask, retard long)
Pour les deux méthodes, si le ScheduleDedexEcutionTime spécifié <= SystemCurrentTime, la tâche sera exécutée immédiatement. ScheduleDexecutiontime ne changera pas en raison de l'exécution excessive d'une tâche.
(2) Schedule (tâche Timertask, date de première fois, longue période), calendrier (tâche Tirmertass, long retard, longue période)
Ces deux méthodes sont un peu différentes des deux ci-dessus. Comme mentionné précédemment, la tâche du temporisateur sera retardée car la tâche précédente est exécutée pendant longtemps. Dans ces deux méthodes, l'heure planifiée de chaque tâche exécutée changera avec l'heure réelle de la tâche précédente, c'est-à-dire ScheduleDeXecutionTime (n + 1) = relexecutiontime (n) + délai d'épilé. C'est-à-dire que si la nième tâche provoque ce processus de temps d'exécution en raison d'une certaine situation, et enfin SystemCurrentTime> = ScheduleDexecutionTime (N + 1), il s'agit de la tâche N + 1 et ne sera pas exécutée en raison du temps. Il attendra l'exécution de la nième tâche avant l'exécution, puis cela mènera inévitablement à la version et à la modification de la mise en œuvre de la mise en œuvre de N + 2 ScheduleDexecutiontime, c'est-à-dire, planifié de temps en temps (n + 2) = relexeCutiontime (n + 1) + périodes. Par conséquent, ces deux méthodes accordent plus d'attention à la stabilité de l'intervalle de stockage.
(3) ScheduleAtFixeDrate (tâche Timertask, date de première fois, longue période), scheduleatFixeDrate (tâche Timertask, long retard, longue période)
Comme mentionné précédemment, l'objectif des méthodes ScheduleAtFixeDrate et Schedule est différente. La méthode de planification se concentre sur la stabilité du temps d'intervalle de sauvegarde, tandis que la méthode ScheduleAtFixeDrate se concentre davantage sur le maintien de la stabilité de la fréquence d'exécution. Pourquoi le dites-vous, les raisons sont les suivantes. Dans la méthode de la planification, le retard de la tâche précédente entraînera le retard de la tâche de synchronisation après, tandis que la méthode ScheduleAtFixeDrate ne le fera pas. Si le temps d'exécution de la nième tâche est trop long, SystemCurrentTime> = ScheduleDexecutionTime (N + 1), il n'y aura pas d'attente pour exécuter la tâche N + 1 immédiatement. Par conséquent, la méthode de calcul de l'heure d'exécution de la méthode ScheduleAtFixeDrate est différente du calendrier, mais ScheduleDExEcutionTime (n) = FirstExecuTime + N * délai d'époque, et la méthode de calcul restera inchangée pour toujours. Par conséquent, ScheduleAtFixeDrate se concentre davantage sur le maintien de la fréquence d'exécution stable.
3. Défauts de la minuterie
3.1. Défauts de la minuterie
La minuterie de la minuterie peut être le temps (exécuter les tâches à l'heure spécifiée), le retard (tâches de retard à 5 secondes) et exécuter périodiquement des tâches (exécuter les tâches à 1 seconde), mais le temporisateur a quelques lacunes. Tout d'abord, la prise en charge de Timer pour la planification est basée sur le temps absolu, pas le temps relatif, il est donc très sensible aux changements de temps du système. Deuxièmement, le fil de minuterie n'attrapera pas d'exceptions. Si l'exception non contrôlée est lancée par Timertask, elle entraînera la fin du fil de minuterie. Dans le même temps, la minuterie ne reprendra pas l'exécution du thread, et il croira à tort que l'ensemble du thread de la minuterie sera annulé. Dans le même temps, Timertask, qui n'a pas encore été exécuté, ne sera plus exécuté et de nouvelles tâches ne peuvent pas être planifiées. Par conséquent, si Timertask lance une exception non contrôlée, Timer produira un comportement imprévisible.
(1) Défauts de retard de gestion du temporisation. Avant que la minuterie ne crée une seule tâche de thread lors de l'exécution d'une tâche chronométrée. S'il y a plusieurs threads, si l'un des threads fait que le temps d'exécution de la tâche de thread est trop long pour une raison quelconque, et l'intervalle entre les deux tâches se produira, certains défauts se produiront:
classe publique TinerTest04 {Timer privé; Public Long Start; public tinmerST04 () {this.timer = new Timer (); start = System.currentTimemillis (); } public void timerOne () {timer.schedule (new TimeMtask () {public void run () {System.out.println ("TimerOn invoqué, le temps:" + (System.currenttimemillis () - Start)); try {Thread.Sleep (4000); // there dort 3000} Catch (interruptedExceptionne }}}}, 1000); } public void timertwo () {timer.schedule (new Timertask () {public void run () {System.out.println ("TimerOne invoqué, le temps:" + (System.CurrentTimemillis () - Start));}}, 3000); } public static void main (String [] args) lève une exception {TIMTERST04 test = new TinerTest04 (); test.timerone (); test.timertwo (); }}Selon notre réflexion normale, Timertwo doit être exécuté après 3s, et le résultat devrait être:
Timerone invoqué, l'heure: 1001 timeone invoqué, l'heure: 3001
Mais les choses se sont opposées à mes attentes. Timerone dort (4000), dort 4 s et la minuterie est à l'intérieur de la minuterie, ce qui provoque le temps nécessaire pour que TimeOne dépasse l'intervalle. Le résultat:
timeone invoqué, l'heure: 1000 timeone invoqué, l'heure: 5000
(2) La minuterie lance un défaut d'exception. Si Timertask lance un RuntimeException, Timer terminera l'exécution de toutes les tâches. comme suit:
classe publique TinerTest04 {Timer privé; public tinmerST04 () {this.timer = new Timer (); } public void timerOne () {timer.schedule (new Timertask () {public void run () {throw new RuntimeException ();}}, 1000); } public void timertwo () {timer.schedule (new Timertask () {public void run () {System.out.println ("vais-je l'exécuter ??");}}, 1000); } public static void main (String [] args) {TIMTERST04 test = new TimerTS04 (); test.timerone (); test.timertwo (); }}Résultat en cours d'exécution: Timerone lance une exception, provoquant la fin de la tâche TimertWo.
Exception dans Thread "TIMER-0" java.lang.runtimeException sur com.chenssy.timer.timertSt04 1 $.
Pour les défauts de la minuterie, nous pouvons considérer ScheduledThreadPoolExecutor en remplacement. Le temporisateur est basé sur le temps absolu et est plus sensible au temps du système, tandis que ScheduledThreadPoolExecutor est basé sur le temps relatif; La minuterie est un seul thread en interne, alors que ScheduledThreadPoolExecutor est un pool de threads en interne, il peut donc prendre en charge l'exécution simultanée de plusieurs tâches.
3.2. Remplacez la minuterie par ScheduleDExecutorService
(1) Résoudre le problème:
classe publique ScheduleDexeCutOrest {private scheduleExECUTORService schedueXec; Public Long Start; ScheduleDexeCutOrest () {this.scheDuexec = exécutor.NewScheduleDThreadPool (2); this.start = System.currenttimemillis (); } public void timerOne () {schedueXec.schedule (new Runnable () {public void run () {System.out.println ("timerOn, le temps:" + (System.currenttimemillis () - start)); try {Thread.Sleep (4000);} cat (interruptedExect e) {e. }, 1000, timeunit.milliseconds); } public void timertwo () {scheduleExec.schedule (new Runnable () {public void run () {System.out.println ("Timertwo, le temps:" + (system.currenttimemillis () - start));}}, 2000, timeunit.MilliseConds); } public static void main (String [] args) {ScheduleDExECUTOrest test = new scheduleDExeCutOrest (); test.timerone (); test.timertwo (); }}Résultats en cours:
Timerone, The Time: 1003 TIMERTWO, The Time: 2005
(2) résoudre le problème deux
classe publique ScheduleDexeCutOrest {private scheduleExECUTORService schedueXec; Public Long Start; ScheduleDexeCutOrest () {this.scheDuexec = exécutor.NewScheduleDThreadPool (2); this.start = System.currenttimemillis (); } public void timerOne () {schedueXec.schedule (new Runnable () {public void run () {throw new RuntimeException ();}}, 1000, timeunit.milliseconds); } public void timertwo () {scheduleExec.scheDuleAtFixeDrate (new Runnable () {public void run () {System.out.println ("Timemertwo invoqué ....." ");}}, 2000 500, TimeUnit.MilliseConds); } public static void main (String [] args) {ScheduleDExECUTOrest test = new scheduleDExeCutOrest (); test.timerone (); test.timertwo (); }}Résultats en cours:
Timertwo invoqué ... Timertwo invoqué ... Timertwo invoqué ... Timertwo invoqué ... Timertwo invoqué ... Timertwo invoqué ... Timertwo invoqué ... Timertwo invoqué ... Timertwo invoqué ... Timertwo invoqué ............ Timertwo invoqué ...............................
4. Utilisez la minuterie pour réaliser le ballon
Un exemple dans le livre de simulation a fait un flipper, qui devait dessiner plusieurs cercles à la position désignée sur la toile, et après une période de retard, il a été repris à une position à proximité. Faites en sorte que la balle semble se déplacer et ajustez le retard à travers le composant jspinner pour contrôler la vitesse de déplacement de la balle.
BallScanvas.java
La classe publique BallScanvas étend Canvas implémente ActionListener, focusListener {Private Ball Balls []; // Timer de minuterie privée de plusieurs balles; Ball de classe statique privée {int x, y; // coordonner la couleur de couleur; // Color Boolean up, à gauche; // Direction de la balle de mouvement (int x, int y, couleur couleur) {this.x = x; this.y = y; this.color = couleur; up = Left = false; }} public BallScanvas (Color Colors [], intlay) {// Initialiser la couleur et retards this.balls = new Ball [Colors.length]; pour (int i = 0, x = 40; i <couleurs.length; i ++, x + = 40) {balls [i] = new ball (x, x, couleurs [i]); } this.addfocusListener (this); timer = new Timer (retard, this); // Créer un objet Timer, Delay Spécifiez le retard Timer.Start (); } // set delay public void setDelay (intlade) {timer.setdelay (delay); } // dessiner public void peinture (graphiques g) {for (int i = 0; i <bals.length; i ++) {g.setColor (balls [i] .color); // Réglez les balles de couleur [i] .x = balles [i] .left? balles [i] .x - 10: balles [i] .x + 10; if (balles [i] .x <0 || balles [i] .x> = this.getWidth ()) {// Changer la direction en boules horizontales [i] .left =! Balls [i] .left; } balles [i] .y = balles [i] .up? Balls [i] .y - 10: balles [i] .y + 10; if (balles [i] .y <0 || balles [i] .y> = this.getheight ()) {// change la direction en billes de direction verticale [i] .up =! Balls [i] .up; } G.FillOval (balles [i] .x, balles [i] .y, 20, 20); // dessine un cercle de diamètre spécifié}} // Timer Timed Exécution Event @Override public void ActionPerformed (ActionEvent E) {Repainte (); // repeindre} // obtenez focus @Override public void focusgained (focUsevent e) {timer.stop (); // Timer Stop} // Lost focus @Override public void focusLost (focUsevent e) {timer.Restart (); // redémarrage de la minuterie}}Ballsjframe.java
Class BallsJFrame étend JFrame implémente ChangeListener {private BallScanvas Ball; Spinner Jspinner privé; public ballsjframe () {super ("finball"); this.setbounds (300, 200, 480, 360); this.setDefaultCloseOperation (exit_on_close); Couleurs Couleurs [] = {Color.Red, Color.Green, Color.Blue, Color.magenta, Color.cyan}; balle = nouveau balle de balle (couleurs, 100); this.getContentPane (). Add (balle); Jpanel Panel = new JPanel (); this.getContentPane (). Add (panneau, "Sud"); PANNEL.ADD (New JLabel ("Delay")); Spinner = new Jspinner (); Spinner.SetValue (100); panneau.add (spinner); Spinner.AddChangeListener (this); this.setVisible (true); } @Override public void statechanged (changeevent e) {// Lors de la modification de la valeur jspinner, cliquez sur le bouton up ou bas de jspinner, ou appuyez sur Entrée dans jspinner ball.setdelay (Integer.Parseint ("" + spinner.getValue ())); } public static void main (string [] args) {new BallsJFrame (); }} Les effets sont les suivants: