scheduleAtFixedRate vs scheduleWithFixedDelay


118

Quelle est la principale différence entre scheduleAtFixedRateet les scheduleWithFixedDelayméthodes de ScheduledExecutorService ?

scheduler.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        System.out.println("scheduleAtFixedRate:    " + new Date());
    }
}, 1, 3L , SECONDS);

scheduler.scheduleWithFixedDelay(new Runnable() {
    @Override
    public void run() {
        System.out.println("scheduleWithFixedDelay: " + new Date());
    }
}, 1, 3L , SECONDS);

ils impriment exactement au même moment, semblent être exécutés exactement au même intervalle.

Réponses:


207

Essayez d'ajouter un Thread.sleep(1000);appel dans votre run()méthode ... Fondamentalement, c'est la différence entre planifier quelque chose en fonction du moment où l'exécution précédente se termine et du moment où elle démarre (logiquement) .

Par exemple, supposons que je programme une alarme pour qu'elle se déclenche avec un taux fixe d'une fois par heure et que chaque fois qu'elle se déclenche, je prends une tasse de café, ce qui prend 10 minutes. Supposons que cela commence à minuit, j'aurais:

00:00: Start making coffee
00:10: Finish making coffee
01:00: Start making coffee
01:10: Finish making coffee
02:00: Start making coffee
02:10: Finish making coffee

Si je programme avec un délai fixe d'une heure, j'aurais:

00:00: Start making coffee
00:10: Finish making coffee
01:10: Start making coffee
01:20: Finish making coffee
02:20: Start making coffee
02:30: Finish making coffee

Celui que vous voulez dépend de votre tâche.


18
Que se passe-t-il dans le scénario de taux fixe s'il faut plus d'une heure pour faire du café?
Brett VanderVeen

5
@BrettVanderVeen: Je pense que cela dépend de l'exécuteur testamentaire en question. Il sera planifié à l'heure - mais l' exécution dépendra de la disponibilité ou non d'un thread pour cet exécuteur. Je vous suggère d'expérimenter pour voir comment cela fonctionne dans divers scénarios.
Jon Skeet

8
@BrettVanderVeen D'après la documentation , "Si une exécution de cette tâche prend plus de temps que sa période, les exécutions suivantes peuvent commencer en retard, mais ne seront pas exécutées simultanément." En d'autres termes, une implémentation conforme ne permettrait pas à la suivante de s'exécuter jusqu'à ce que la précédente se termine.
M. Justin

Pouvez-vous fournir un code de travail pour la sortie affichée (café) pour un novice comme moi?
MuneshSingh

@MuneshSingh: Pas dans cette question, qui demande d'expliquer quelle est la différence entre la programmation à un taux fixe et la planification à un délai fixe. De toute façon, vous ne l'implémenteriez pas vous-même - vous utiliseriez les exécuteurs intégrés.
Jon Skeet

57

Visualisez la série chronologique de la scheduleAtFixedRateméthode d' appel . Les prochaines exécutions commenceront immédiatement si la dernière prend plus de temps que la période. Sinon, il démarrera après la période.

série chronologique de la méthode scheduleAtFixedRate d'appel

Série chronologique de la scheduleWithFixedDelayméthode d' appel . La prochaine exécution commencera après un délai entre la fin d'une exécution et le début de la suivante, indépendamment de son heure d'exécution

série chronologique de la méthode scheduleWithFixedDelay

L'espoir peut vous aider


Je ne pouvais pas comprendre le mot «supplémentaire» mentionné dans le diagramme de série chronologique de scheduleAtFixedRate.
MuneshSingh

1
@MuneshSingh Il est destiné à montrer que le temps d'exécution de la tâche est plus long que prévu, donc cela prend du temps "supplémentaire" et la prochaine exécution commence immédiatement.
Viorel

@Viorel merci pour cette clarification. Cela signifie-t-il que "période" n'est pas exactement un délai fixe entre deux exécutions consécutives.
MuneshSingh

1
@MuneshSingh La période est fixe, mais elle n'arrêtera pas la tâche en cours une fois qu'elle l'aura passée, il n'y aurait tout simplement aucun délai entre cette exécution et la suivante. Si vous voulez créer un "timeout", vous voudrez peut-être conserver le futur et l'annuler dans un autre exécuteur. En termes simples, il dit commencer la première exécution et la suivante dès que possible après que le temps "période" se soit écoulé .
Viorel le

4

La scheduleAtFixedRate()méthode crée une nouvelle tâche et la soumet à l'exécuteur à chaque période, que la tâche précédente soit terminée ou non .

D'autre part, la scheduleWithFixedDelay()méthode crée une nouvelle tâche une fois la tâche précédente terminée .


Vous avez écrit deux fois scheduleAtFixedRate:)
Vlad

3

Si vous lisez le Java Doc, ce sera plus clair

ScheduledFuture scheduleAtFixedRate (Commande exécutable, long initialDelay, longue période, unité TimeUnit) Crée et exécute une action périodique qui devient activée en premier après le délai initial donné, puis avec la période donnée; c'est-à-dire que les exécutions commenceront après initialDelay puis initialDelay + period, puis initialDelay + 2 * period, et ainsi de suite.

ScheduledFuture scheduleWithFixedDelay (commande Runnable, long initialDelay, long delay, TimeUnit unit) Crée et exécute une action périodique qui devient activée d'abord après le délai initial donné, puis avec le délai donné entre la fin d'une exécution et le début de la suivante.


1

Il y a un problème dans scheduleAtFixedRate si le premier thread prend trop de temps et n'est pas terminé dans une durée donnée, le second thread conscutif ne démarrera pas une fois que la première tâche sera terminée et ne démarrera pas immédiatement tant que le premier thread a terminé sa tâche et sa durée s'est écoulé. JVM décidera quand la prochaine tâche sera exécutée.

Je pense que cela vous aidera à choisir la méthode car à cause de cela, j'ai un gros problème


1
Quoi? JVM décidera? Qu'est-ce que c'est censé vouloir dire? Il est vrai que le runnable ne sera pas exécuté en même temps que lui-même selon la documentation, mais c'est décidé par l'exécuteur, ce qui peut être personnalisé OU le standard ScheduledThreadPoolExecutor(et ce dernier a un comportement bien défini)
Ordous

Non, j'ai trouvé un problème similaire dans mon application où j'ai donné un intervalle de 15 minutes et la première tâche ne se termine pas dans 15 minutes et prend 15,30 secondes, donc la deuxième tâche n'a pas commencé immédiatement après 5 minutes et quelque temps après 8 min et je ne sais pas si nous pouvons contrôler ce comportement car ce n'est pas un comportement standard.
user1047873

Cela ressemble à une file d'attente de tâches de manuel.
Ordous

Oui, cela signifie simplement que tous les threads de votre exécuteur sont déjà occupés à faire quelque chose et que votre tâche est placée dans une file d'attente de choses à faire. ( REMARQUE vous devez le confirmer en regardant ladite file d'attente ou en regardant ce que font les threads de l'exécuteur). La façon dont vous contrôlez cela dépend du type d'exécuteur testamentaire dont vous disposez. Vous voudrez peut-être créer un exécuteur 1 thread distinct pour cette tâche particulière uniquement, alors il n'attendra rien. Ou donnez à votre exécuteur actuel plus de threads. Ou changez de stratégie.
Ordous

0

Écrivons un programme simple:

import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

var time = 0L
var start = System.currentTimeMillis()
val executor = Executors.newScheduledThreadPool(1)
executor.scheduleWithFixedDelay({
    if (time >= 12_000L) {
        executor.shutdown()
    } else {
        Thread.sleep(2000L)
        val now = System.currentTimeMillis()
        time += now - start
        System.out.println("Total $time delay ${now - start}\n")
        start = now
    }
}, 0L, 1000L, TimeUnit.MILLISECONDS)

Et voyez les résultats:

| scheduleWithFixedDelay |   scheduleAtFixedRate  |
|:----------------------:|:----------------------:|
| Total 2001 delay 2001  | Total 2003 delay 2003  |
| Total 5002 delay 3001  | Total 4004 delay 2001  |
| Total 8003 delay 3001  | Total 6004 delay 2000  |
| Total 11003 delay 3000 | Total 8004 delay 2000  |
| Total 14003 delay 3000 | Total 10005 delay 2001 |
|          ---           | Total 12005 delay 2000 |

AVIS le temps d'exécution est plus long que l'attente

scheduleWithFixedDelay conserve le délai de
programmationAtFixedRate supprime le délai


-1
scheduledExecutorService.scheduleAtFixedRate(() -> {
        System.out.println("runnable start"); try { Thread.sleep(5000);  System.out.println("runnable end");} catch
     (InterruptedException e) { // TODO Auto-generated catch block
      e.printStackTrace(); }}, 2, 7, TimeUnit.SECONDS);



     scheduledExecutorService.scheduleWithFixedDelay(() -> {
     System.out.println("runnable start"); try { Thread.sleep(5000); System.out.println("runnable end");} catch
     (InterruptedException e) { // TODO Auto-generated catch block
     e.printStackTrace(); } }, 2, 7, TimeUnit.SECONDS);

Exécutez-le simplement et vous saurez la différence. Je vous remercie


1
Veuillez également expliquer comment le code résout le problème de l'OP. :)
Yash
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.