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.

Serrador
fonte

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.

Jon Skeet
fonte
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

Ken Block
fonte
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 .

Imar
fonte
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.

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

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

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

logi tech
fonte
1
Explique também como o código resolve o problema do OP. :)
Yash