scheduleAtFixedRate vs scheduleWithFixedDelay

117

¿Cuál es la principal diferencia entre los métodos scheduleAtFixedRatey scheduleWithFixedDelayde 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);

imprimen exactamente al mismo tiempo, parece que se ejecutan exactamente en el mismo intervalo.

Aserrador
fuente

Respuestas:

206

Intente agregar una Thread.sleep(1000);llamada dentro de su run()método ... Básicamente, es la diferencia entre programar algo en función de cuándo termina la ejecución anterior y cuándo comienza (lógicamente) .

Por ejemplo, supongamos que programo una alarma para que suene con una frecuencia fija de una vez por hora, y cada vez que suena, tomo una taza de café, que tarda 10 minutos. Supongamos que comienza a la medianoche, tendría:

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 programo con un retraso fijo de una hora, tendría:

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

Cuál quieres depende de tu tarea.

Jon Skeet
fuente
18
¿Qué sucede en el escenario de tarifa fija si se tarda más de una hora en preparar café?
Brett VanderVeen
5
@BrettVanderVeen: Creo que depende del albacea en cuestión. Se programará a tiempo, pero si se ejecuta depende de si un hilo está disponible para ese ejecutor o no. Le sugiero que experimente para ver cómo funciona esto en varios escenarios.
Jon Skeet
8
@BrettVanderVeen De la documentación , "Si la ejecución de esta tarea tarda más que su período, es posible que las ejecuciones posteriores comiencen tarde, pero no se ejecutarán simultáneamente". En otras palabras, una implementación conforme no permitiría que se ejecute la siguiente hasta que finalice la anterior.
M. Justin
¿Puede proporcionar un código de trabajo para la salida que se muestra (café) para un novato como yo?
MuneshSingh
@MuneshSingh: No en esta pregunta, que pide explicar cuál es la diferencia entre programar a una tarifa fija y programar a un retraso fijo. Sin embargo, no implementaría esto usted mismo de todos modos, usaría los ejecutores integrados.
Jon Skeet
57

Visualice series de tiempo del scheduleAtFixedRatemétodo de invocación . Las siguientes ejecuciones comenzarán inmediatamente si la última lleva más de un período. De lo contrario, comenzará después del período de tiempo.

Serie temporal del método ScheduleAtFixedRate de invocación

Serie temporal del scheduleWithFixedDelaymétodo de invocación . La siguiente ejecución comenzará después del tiempo de demora entre la terminación de una ejecución y el comienzo de la siguiente, independientemente de su tiempo de ejecución.

serie temporal del método de invocación scheduleWithFixedDelay

La esperanza puede ayudarte

bloque Ken
fuente
No pude entender la palabra "extra" mencionada en el diagrama de series de tiempo scheduleAtFixedRate.
MuneshSingh
1
@MuneshSingh Está destinado a mostrar que el tiempo de ejecución de la tarea es más largo de lo programado, por lo que se necesita tiempo "extra" y la siguiente ejecución comienza de inmediato.
Viorel
@Viorel gracias por aclarar. ¿Significa eso que "período" no es exactamente un retraso de tiempo fijo entre dos ejecuciones consecutivas?
MuneshSingh
1
@MuneshSingh El período es fijo, pero no detendrá la tarea actual una vez que la pase, simplemente no habrá demora entre esta ejecución y la siguiente. Si desea crear un "tiempo de espera", es posible que desee retener el futuro y cancelarlo en un ejecutor diferente. En palabras simples, dice comenzar la primera ejecución y la siguiente tan pronto como sea posible después de que pase el tiempo del "período" .
Viorel
4

El scheduleAtFixedRate()método crea una nueva tarea y la envía al ejecutor cada período, independientemente de si la tarea anterior terminó o no .

Por otro lado, el scheduleWithFixedDelay()método crea una nueva tarea una vez finalizada la anterior .

Imar
fuente
Escribiste dos veces scheduleAtFixedRate:)
Vlad
3

Si lee Java Doc, será más claro

ScheduledFuture scheduleAtFixedRate (Comando ejecutable, retraso inicial largo, período largo, unidad TimeUnit) Crea y ejecuta una acción periódica que se habilita primero después del retraso inicial dado, y luego con el período dado; es decir, las ejecuciones comenzarán después de initialDelay, luego initialDelay + período, luego initialDelay + 2 * período, y así sucesivamente.

ScheduledFuture scheduleWithFixedDelay (comando ejecutable, retraso inicial largo, retraso largo, unidad TimeUnit) Crea y ejecuta una acción periódica que se habilita primero después del retraso inicial dado, y posteriormente con el retraso dado entre la terminación de una ejecución y el comienzo de la siguiente.

shazin
fuente
1

Hay un problema en scheduleAtFixedRate si el primer hilo está tardando demasiado y no finaliza en una duración determinada, entonces el segundo hilo consecutivo no se iniciará una vez que la primera tarea haya finalizado y no se iniciará inmediatamente mientras el primer hilo haya completado su tarea y haya alcanzado la duración ha transcurrido. JVM decidirá cuándo se ejecutará la siguiente tarea.

Creo que eso te ayudará a elegir el método porque debido a esto tengo un gran problema

usuario1047873
fuente
1
¿Qué? ¿JVM decidirá? ¿Qué se supone que significa eso? Es cierto que el ejecutable no se ejecutará simultáneamente consigo mismo según los documentos, pero lo decide el ejecutor, que puede ser personalizado O el estándar ScheduledThreadPoolExecutor(y este último tiene un comportamiento bien definido)
Ordous
No, he encontrado un problema similar en mi aplicación en el que he dado un intervalo de 15 minutos y la primera tarea no se termina en 15 minutos y toma 15.30 segundos, por lo que la segunda tarea no se inició de inmediato. 8 min y no sé si podemos controlar este comportamiento, ya que no es un comportamiento estándar.
user1047873
Eso suena a cola de tareas de libro de texto.
Ordous
Sí, solo significa que todos los subprocesos de su ejecutor ya están ocupados haciendo algo, y su tarea se pone en una cola de cosas por hacer. ( NOTA : debe confirmar esto mirando dicha cola o mirando lo que están haciendo los subprocesos ejecutores). Cómo controle esto depende del tipo de albacea que tenga. Es posible que desee crear un ejecutor de 1 hilo por separado solo para esta tarea en particular, entonces no esperará nada. O dale más hilos a tu ejecutor actual. O cambiar su estrategia.
Ordous
0

Escribamos un programa 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)

Y mira los resultados:

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

AVISO el tiempo de ejecución es mayor que esperar

scheduleWithFixedDelay mantiene la demora
scheduleAtFixedRate elimina la demora

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

Simplemente ejecútelo y sabrá la diferencia. Gracias

tecnología logi
fuente
1
También explique cómo el código resuelve el problema del OP. :)
Yash