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.