scheduleAtFixedRate vs scheduleWithFixedDelay


117

Qual é a principal diferença entre scheduleAtFixedRatee scheduleWithFixedDelaymétodos 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);

eles imprimem exatamente ao mesmo tempo, parece que são executados exatamente no mesmo intervalo.

Respostas:


206

Tente adicionar uma Thread.sleep(1000);chamada dentro do seu run()método ... Basicamente, é a diferença entre agendar algo baseado em quando a execução anterior termina e quando ela (logicamente) começa .

Por exemplo, suponha que eu programe um alarme para disparar a uma taxa fixa de uma vez por hora e, toda vez que ele disparar, tomo uma xícara de café, o que leva 10 minutos. Suponha que comece à meia-noite, eu teria:

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 eu agendar com um atraso fixo de uma hora, eu teria:

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

Qual você deseja depende da sua tarefa.


18
O que acontece no cenário fixedRate se levar mais de uma hora para fazer café?
— Brett VanderVeen

5
@BrettVanderVeen: Acredito que depende do executor em questão. Será agendado na hora certa - mas se isso será executado depende se um thread está ou não disponível para aquele executor. Eu sugiro que você experimente para ver como isso funciona em vários cenários.
— Jon Skeet

8
@BrettVanderVeen Da documentação , "Se qualquer execução desta tarefa demorar mais do que seu período, as execuções subsequentes podem começar atrasadas, mas não serão executadas simultaneamente." Em outras palavras, uma implementação conforme não permitiria que a próxima fosse executada até que a anterior terminasse.
— M. Justin

Você pode fornecer um código funcional para a saída mostrada (café) para um novato como eu?
— MuneshSingh

@MuneshSingh: Não nesta pergunta, que pede para explicar qual é a diferença entre programar com uma taxa fixa e programar com um atraso fixo. Porém, você não implementaria isso sozinho - usaria os executores integrados.
— Jon Skeet,

57

Visualize séries temporais do scheduleAtFixedRatemétodo de invocação . As próximas execuções começarão imediatamente se a última demorar mais do que o período. Caso contrário, ele começará após o período.

série temporal do método de chamada scheduleAtFixedRate

Séries temporais do scheduleWithFixedDelaymétodo de invocação . A próxima execução começará após o tempo de atraso entre o término de uma execução e o início da próxima, independentemente do seu tempo de execução

série temporal do método scheduleWithFixedDelay de invocação

Espero poder te ajudar


Não consegui entender a palavra "extra" mencionada no diagrama de série temporal scheduleAtFixedRate.
— MuneshSingh

1
@MuneshSingh Destina-se a mostrar que o tempo de execução da tarefa é maior do que o programado, por isso leva um tempo "extra" e a próxima execução começa imediatamente.
— Viorel

@Viorel obrigado pelo esclarecimento. Isso significa que "período" não é exatamente um atraso de tempo fixo entre duas execuções consecutivas.
— MuneshSingh

1
@MuneshSingh O período é fixo, mas não interromperá a tarefa atual uma vez que o passe, simplesmente não haverá atraso entre esta execução e a próxima. Se você quiser criar um "tempo limite", convém reter o Future e cancelá-lo em um executor diferente. Em palavras simples, ele diz para iniciar a primeira execução e a próxima assim que possível após o "período" passar .
— Viorel

4

O scheduleAtFixedRate()método cria uma nova tarefa e a envia ao executor a cada período, independentemente de a tarefa anterior ter sido concluída ou não .

Por outro lado, o scheduleWithFixedDelay()método cria uma nova tarefa após o término da tarefa anterior .


Você escreveu duas vezes scheduleAtFixedRate:)
— Vlad

3

Se você ler o Java Doc, será mais claro

ScheduledFuture scheduleAtFixedRate (comando Runnable, long initialDelay, longo período, unidade TimeUnit) Cria e executa uma ação periódica que é habilitada primeiro após o atraso inicial fornecido e, subsequentemente, com o período determinado; isto é, as execuções começarão após initialDelay, initialDelay + período, initialDelay + 2 * período e assim por diante.

ScheduledFuture scheduleWithFixedDelay (comando Runnable, long initialDelay, long delay, unidade TimeUnit) Cria e executa uma ação periódica que se torna habilitada primeiro após o determinado atraso inicial e, subsequentemente, com o determinado atraso entre o término de uma execução e o início da próxima.


1

Existe um problema em scheduleAtFixedRate se o primeiro thread está demorando muito e não terminou com a duração dada então o segundo thread consciencial não iniciará uma vez que a primeira tarefa será concluída e não será iniciada imediatamente enquanto o primeiro thread terminar sua tarefa e receber a duração foi decorrido. JVM decidirá quando a próxima tarefa será executada.

Acho que vai te ajudar a escolher o método Porque devido a isso eu tenho um grande problema


1
O que? JVM vai decidir? O que isso quer dizer? É verdade que o executável não será executado simultaneamente com ele mesmo de acordo com os documentos, mas é decidido pelo executor, que pode ser personalizado OU o padrão ScheduledThreadPoolExecutor(e este último tem um comportamento bem definido)
— Ordous

Não, eu encontrei um problema semelhante em meu aplicativo, onde dei um intervalo de 15 minutos e a primeira tarefa não está sendo concluída em 15 minutos e leva 15,30 segundos, então a segunda tarefa não começou imediatamente, algum tempo começou após 5 minutos e algum tempo depois 8 min e não estou ciente de se podemos controlar esse comportamento, pois este não é um comportamento padrão.
— user1047873

Isso soa como uma fila de tarefas de livro didático.
— Ordous

Sim, significa apenas que todos os threads em seu executor já estão ocupados fazendo algo, e sua tarefa é colocada em uma fila de coisas a fazer. ( NOTA você precisa confirmar isso olhando para a referida fila ou observando o que as threads do executor estão fazendo). Como você controla isso depende do tipo de executor que você tem. Você pode querer fazer um executor de 1 thread separado apenas para esta tarefa específica, então ele não vai esperar nada. Ou dê ao seu executor atual mais threads. Ou mude sua estratégia.
— Ordous

0

Vamos escrever um programa simples:

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 veja os 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 o tempo de execução é maior do que a espera

scheduleWithFixedDelay mantém atraso
scheduleAtFixedRate remove atraso


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

Basta executá-lo e você saberá a diferença. Obrigado


1
Explique também como o código resolve o problema do OP. :)
— Yash
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.