scheduleAtFixedRate против scheduleWithFixedDelay


118

Что главное различие между scheduleAtFixedRateи scheduleWithFixedDelayметодами 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);

они печатают точно в одно и то же время, кажется, они выполняются с точно таким же интервалом.

Ответы:


207

Попробуйте добавить Thread.sleep(1000);вызов в свой run()метод ... В основном это разница между планированием чего-либо на основе того, когда заканчивается предыдущее выполнение, и когда оно (логически) начинается .

Например, предположим, что я планирую срабатывать будильник с фиксированной частотой один раз в час, и каждый раз, когда он срабатывает , я выпиваю чашку кофе, что занимает 10 минут. Предположим, что это начнется в полночь, у меня будет:

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

Если я планирую с фиксированной задержкой в один час, у меня будет:

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

Какой из них вы хотите, зависит от вашей задачи.


18
Что происходит в сценарии fixedRate, если на приготовление кофе уходит больше часа?
— Бретт Вандервин,

5
@BrettVanderVeen: Я считаю, что это зависит от конкретного исполнителя. Это будет запланировано на время - но что ли выполняет зависит от того , или не доступна для этого исполнителя нить. Я предлагаю вам поэкспериментировать, чтобы увидеть, как это работает в различных сценариях.
— Джон Скит,

8
@BrettVanderVeen Из документации : «Если выполнение этой задачи занимает больше времени, чем ее период, то последующие выполнения могут начаться с опозданием, но не будут выполняться одновременно». Другими словами, соответствующая реализация не позволит выполнить следующую, пока не завершится предыдущая.
— M. Justin

Можете ли вы предоставить рабочий код показанного результата (кофе) для такого новичка, как я?
— MuneshSingh

@MuneshSingh: Не в этом вопросе, который просит объяснить, в чем разница между планированием с фиксированной скоростью и планированием с фиксированной задержкой. Однако вы бы все равно не реализовали это сами - вы бы использовали встроенные исполнители.
— Джон Скит

57

Визуализируйте временной ряд scheduleAtFixedRateметода вызова . Следующие казни начнутся немедленно, если последнее длится дольше периода. В противном случае он начнется по истечении определенного периода времени.

временной ряд вызова метода scheduleAtFixedRate

Временной ряд scheduleWithFixedDelayметода вызова . Следующее выполнение начнется по истечении времени задержки между завершением одного выполнения и началом следующего, независимо от времени его выполнения.

временной ряд вызова метода scheduleWithFixedDelay

Надеюсь помочь тебе


Я не мог понять "лишнее" слово, упомянутое на диаграмме временных рядов scheduleAtFixedRate.
— MuneshSingh

1
@MuneshSingh Он предназначен для того, чтобы показать, что время выполнения задачи больше запланированного, поэтому требуется «дополнительное» время, и следующее выполнение начинается немедленно.
— Виорел

@Viorel, спасибо за разъяснения. Означает ли это, что «период» - это не совсем фиксированная задержка между двумя последовательными исполнениями.
— MuneshSingh

1
@MuneshSingh Период фиксирован, но он не остановит текущую задачу после ее прохождения, просто не будет задержки между этим запуском и следующим. Если вы хотите создать «тайм-аут», вы можете сохранить Future и отменить его в другом исполнителе. Проще говоря, он говорит начать первое выполнение, а следующее - как можно скорее по прошествии "периода" времени .
— Виорел

4

scheduleAtFixedRate()Метод создает новую задачу и передает его исполнителю за каждый период, независимо от того , или не закончили предыдущее задание .

С другой стороны, scheduleWithFixedDelay()метод создает новую задачу после завершения предыдущей .


Вы писали дважды scheduleAtFixedRate:)
— Влад

3

Если вы прочитаете Java Doc, будет понятнее

ScheduledFuture scheduleAtFixedRate (Runnable command, long initialDelay, long period, TimeUnit unit) Создает и выполняет периодическое действие, которое активируется сначала после заданной начальной задержки, а затем с заданным периодом; то есть выполнение начнется после initialDelay, затем initialDelay + period, затем initialDelay + 2 * period и так далее.

ScheduledFuture scheduleWithFixedDelay (Runnable command, long initialDelay, long delay, TimeUnit unit) Создает и выполняет периодическое действие, которое активируется сначала после заданной начальной задержки, а затем с заданной задержкой между завершением одного выполнения и началом следующего.


1

В scheduleAtFixedRate есть одна загвоздка, если первый поток занимает слишком много времени и не завершился в течение заданного времени, тогда второй последовательный поток не запустится, как только первая задача будет завершена, и не будет немедленно запущен, пока первый поток завершил свою задачу и продолжительность gievn истекло. JVM решит, когда будет выполнена следующая задача.

Думаю, это поможет вам выбрать метод Becuase, в связи с чем у меня возникла большая проблема


1
Какой? JVM решит? Что это вообще должно значить? Это правда, что runnable не будет выполняться одновременно с самим собой в соответствии с документами, но это решает исполнитель, который может быть индивидуальным ИЛИ стандартным ScheduledThreadPoolExecutor(и последний имеет четко определенное поведение)
— Ordous

Нет, я обнаружил аналогичную проблему в своем приложении, где я дал 15-минутный интервал, а первая задача не была завершена через 15 минут и занимает 15.30 секунд, поэтому вторая задача не запускалась немедленно, она запускалась через 5 минут и через некоторое время после 8 мин, и я не знаю, можем ли мы контролировать это поведение, поскольку это нестандартное поведение.
— user1047873 09

Это похоже на постановку в очередь задач из учебника.
— Ordous 09

Да, это просто означает, что все потоки в вашем исполнителе уже заняты чем-то, и ваша задача помещается в очередь дел, которые нужно сделать. ( ПРИМЕЧАНИЕ, вам необходимо подтвердить это, просмотрев указанную очередь или посмотрев, что делают потоки исполнителя). Как вы это контролируете, зависит от того, какой у вас исполнитель. Вы можете создать отдельный однопоточный исполнитель только для этой конкретной задачи, тогда он не будет ничего ждать. Или дайте вашему текущему исполнителю больше потоков. Или измените стратегию.
— Ordous 09

0

Напишем простую программу:

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)

И посмотрите результаты:

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

ВНИМАНИЕ, время выполнения больше, чем ожидание

scheduleWithFixedDelay сохраняет задержку
scheduleAtFixedRate удаляет задержку


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

Просто выполните его, и вы почувствуете разницу. Спасибо


1
Также объясните, как код решает проблему OP. :)
— Яш
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.