scheduleAtFixedRate vs scheduleWithFixedDelay


117

Qual è la principale differenza tra scheduleAtFixedRatee i scheduleWithFixedDelaymetodi di 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);

stampano esattamente allo stesso tempo, sembra che siano eseguiti esattamente allo stesso intervallo.

Risposte:


206

Prova ad aggiungere una Thread.sleep(1000);chiamata all'interno del tuo run()metodo ... Fondamentalmente è la differenza tra programmare qualcosa in base a quando termina l' esecuzione precedente e quando (logicamente) inizia .

Ad esempio, supponiamo che io programmi una sveglia in modo che si attivi con una frequenza fissa di una volta ogni ora, e ogni volta che suona, bevo una tazza di caffè, che impiega 10 minuti. Supponiamo che inizi a mezzanotte, avrei:

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

Se pianifico con un ritardo fisso di un'ora, avrei:

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

Quale vuoi dipende dal tuo compito.


18
Cosa succede nello scenario fixedRate se ci vuole più di un'ora per preparare il caffè?
Brett VanderVeen

5
@BrettVanderVeen: credo che dipenda dall'esecutore in questione. Verrà pianificato in tempo, ma il fatto che venga eseguito dipende dalla disponibilità o meno di un thread per quell'esecutore. Ti suggerisco di sperimentare per vedere come funziona in vari scenari.
Jon Skeet

8
@BrettVanderVeen Dalla documentazione , "Se l'esecuzione di questa attività richiede più tempo del periodo, le esecuzioni successive potrebbero iniziare in ritardo, ma non verranno eseguite contemporaneamente." In altre parole, un'implementazione conforme non consentirebbe l'esecuzione di quella successiva fino al termine di quella precedente.
M. Justin

Potete fornire un codice funzionante per l'output mostrato (caffè) per un principiante come me?
MuneshSingh

@MuneshSingh: Non in questa domanda, che chiede di spiegare qual è la differenza tra la pianificazione a una tariffa fissa e la pianificazione con un ritardo fisso. Tuttavia, non lo implementeresti da solo: useresti gli esecutori incorporati.
Jon Skeet

57

Visualizza le serie temporali del scheduleAtFixedRatemetodo di invocazione . Le esecuzioni successive inizieranno immediatamente se l'ultima impiega più del periodo. Altrimenti, inizierà dopo il periodo di tempo.

serie temporale del metodo scheduleAtFixedRate di chiamata

Serie temporale del scheduleWithFixedDelaymetodo di chiamata . La prossima esecuzione inizierà dopo il tempo di ritardo tra la fine di un'esecuzione e l'inizio della successiva, indipendentemente dal tempo di esecuzione

serie temporale del metodo scheduleWithFixedDelay di chiamata

La speranza può aiutarti


Non riuscivo a capire la parola "extra" menzionata nel diagramma delle serie temporali scheduleAtFixedRate.
MuneshSingh

1
@MuneshSingh Ha lo scopo di mostrare che il tempo di esecuzione dell'attività è più lungo del previsto, quindi ci vuole tempo "extra" e la prossima esecuzione inizia subito.
Viorel

@Viorel grazie per aver chiarito. Significa che "periodo" non è esattamente un intervallo di tempo fisso tra due esecuzioni consecutive.
MuneshSingh

1
@MuneshSingh Il periodo è fisso, ma non interromperà l'attività corrente una volta superata, semplicemente non ci sarebbe alcun ritardo tra questa esecuzione e la successiva. Se vuoi creare un "timeout" potresti voler conservare il futuro e cancellarlo in un altro esecutore. In parole semplici, dice di iniziare la prima esecuzione e quella successiva il prima possibile dopo che è trascorso il "periodo" .
Viorel

4

Il scheduleAtFixedRate()metodo crea una nuova attività e la invia all'esecutore ogni periodo, indipendentemente dal fatto che l'attività precedente sia terminata o meno .

D'altra parte, il scheduleWithFixedDelay()metodo crea una nuova attività al termine dell'attività precedente .


Hai scritto due volte scheduleAtFixedRate:)
Vlad il

3

Se leggi il documento Java sarà più chiaro

ScheduledFuture scheduleAtFixedRate (Comando eseguibile, long initialDelay, long period, unità TimeUnit) Crea ed esegue un'azione periodica che viene abilitata prima dopo il ritardo iniziale specificato e successivamente con il periodo specificato; cioè le esecuzioni inizieranno dopo initialDelay, quindi initialDelay + period, quindi initialDelay + 2 * period e così via.

ScheduledFuture scheduleWithFixedDelay (Comando eseguibile, long initialDelay, long delay, TimeUnit unit) Crea ed esegue un'azione periodica che viene abilitata prima dopo il ritardo iniziale specificato e successivamente con il ritardo dato tra la fine di un'esecuzione e l'inizio di quella successiva.


1

C'è un problema in scheduleAtFixedRate se il primo thread sta impiegando troppo tempo e non è terminato in una data durata, il secondo thread consecutivo non si avvierà una volta terminata la prima attività e non verrà avviato immediatamente mentre il primo thread ha completato il compito e la durata del gievn è trascorso. JVM deciderà quando verrà eseguita la prossima attività.

Penso che ti aiuterà a scegliere il metodo perché a causa di questo ho un grosso problema


1
Che cosa? JVM deciderà? Cosa dovrebbe significare? È vero che il eseguibile non verrà eseguito contemporaneamente a se stesso come da documenti, ma è deciso dall'esecutore, che può essere personalizzato O lo standard ScheduledThreadPoolExecutor(e quest'ultimo ha un comportamento ben definito)
Ordous

No, ho riscontrato un problema simile nella mia applicazione in cui ho dato un intervallo di 15 minuti e la prima attività non viene completata in 15 minuti e impiega 15,30 secondi, quindi la seconda attività non è stata avviata immediatamente, qualche volta inizia dopo 5 minuti e qualche tempo dopo 8 min e non sono a conoscenza del fatto che possiamo controllare questo comportamento in quanto questo non è un comportamento standard.
user1047873

Sembra una coda di attività da manuale.
Ordous

Sì, significa solo che tutti i thread nel tuo esecutore sono già impegnati a fare qualcosa e il tuo compito viene messo in una coda di cose da fare. ( NOTA: è necessario confermarlo guardando la suddetta coda o guardando cosa stanno facendo i thread dell'esecutore). Il modo in cui lo controlli dipende dal tipo di esecutore che hai. Potresti voler creare un esecutore a 1 thread separato solo per questa particolare attività, quindi non aspetterà nulla. Oppure dai al tuo attuale esecutore più thread. Oppure cambia la sua strategia.
Ordous

0

Scriviamo un semplice programma:

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)

E guarda i risultati:

| 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 |

AVVISO il tempo di esecuzione è maggiore dell'attesa

scheduleWithFixedDelay mantiene il ritardo
scheduleAtFixedRate rimuove il ritardo


-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);

Eseguilo e conoscerai la differenza. Grazie


1
Spiega anche come il codice risolve il problema dell'OP. :)
Yash
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.