rasporedAtFixedRate проти розкладуWithFixedDelay


117

У чому головна відмінність між 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);

вони друкують точно в той самий час, здається, вони виконуються з точно тим же інтервалом.

Відповіді:


206

Спробуйте додати 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
Що відбувається за сценарієм фіксованого рейтингу, якщо на приготування кави потрібно більше години?
Бретт ВандерВін

5
@BrettVanderVeen: Я вважаю, що це залежить від виконавця, про який йде мова. Це буде заплановано вчасно - але від того, чи виконується це виконання, залежить від того, чи доступний потоку для цього виконавця чи ні. Я пропоную вам експериментувати, щоб побачити, як це працює в різних сценаріях.
Джон Скіт

8
@BrettVanderVeen З документації , "Якщо будь-яке виконання цього завдання займає більше часу, ніж його період, то наступні виконання можуть розпочатися із запізненням, але вони одночасно не виконуватимуться". Іншими словами, відповідна реалізація не дозволить виконати наступну, поки не завершиться попередня.
М. Джастін

Чи можете ви надати робочий код для показаного результату (кава) для початківця, як я?
MuneshSingh

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

57

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

часовий ряд розкладу виклику методAtFixedRate

scheduleWithFixedDelayМетод часових рядів виклику . Наступне виконання розпочнеться після затримки між закінченням одного виконання та початком наступного, незалежно від часу його виконання

часовий ряд графіку виклику методомWithFixedDelay

Надія може вам допомогти


Я не міг зрозуміти "зайве" слово, згадане в діаграмі часових рядів rasporedAtFixedRate.
MuneshSingh

1
@MuneshSingh Мається на увазі показати, що час виконання завдання довший, ніж запланований, тому він займає "додатковий" час, і наступне виконання починається відразу.
Віорель

@Viorel дякую за уточнення. Чи означає це, що "період" не є точно фіксованою затримкою часу між двома послідовними стратами.
MuneshSingh

1
@MuneshSingh Період фіксований, але він не зупинить поточну задачу, як тільки вона буде виконана, просто не буде затримки між цим запуском та наступним. Якщо ви хочете створити "тайм-аут", ви можете зберегти Майбутнє та скасувати його у іншого виконавця. Простими словами, він говорить, що почати перше виконання, а наступне якомога швидше, коли пройде "період" .
Віорель

4

scheduleAtFixedRate()Метод створює нову задачу і передає його виконавцю за кожен період, незалежно від того , чи не закінчили попереднє завдання .

З іншого боку, scheduleWithFixedDelay()метод створює нове завдання після завершення попереднього завдання .


Ви писали двічі scheduleAtFixedRate:)
Влад

3

Якщо ви прочитаєте Java Doc, то буде зрозуміліше

ScheduledFuture rasporedAtFixedRate (команда Runnable, довгий початковий затримка, тривалий період, одиниця TimeUnit) Створює та виконує періодичну дію, яка стає включеною спочатку після заданої початкової затримки, а згодом із заданим періодом; тобто виконання розпочнеться після початкового затримки, а потім початкового періоду + періоду, потім початкового періоду + 2 * періоду тощо.

ScheduledFuture rasporedWithFixedDelay (команда Runnable, довгий початковий затримка, велика затримка, одиниця TimeUnit) Створює та виконує періодичну дію, яка стає включеною спочатку після заданої початкової затримки, а згодом із заданою затримкою між припиненням одного виконання та початком наступного.


1

У програмі rasporedAtFixedRate є одна фіксація, якщо перший потік забирається занадто довго і не закінчується за задану тривалість, тоді другий споживчий потік не запускається, коли перше завдання буде закінчено, і він не розпочнеться негайно, поки перша нитка виконала їх завдання та тривалість. минуло. JVM вирішить, коли буде виконано наступне завдання.

Я думаю, що це допоможе вам вибрати метод Becuase, через це у мене виникли великі проблеми


1
Що? JVM вирішить? Що це навіть має означати? Це правда, що запускається не буде виконуватися одночасно із самим собою, як за документами, але це вирішує виконавець, який може бути на замовлення АБО стандарт ScheduledThreadPoolExecutor(а останній має чітко визначену поведінку)
Звичайний

Ні, я не знайшов подібну проблему в своїй програмі, де я дав інтервал 15 хвилин, і перше завдання не закінчується через 15 хв і займає 15,30 сек, щоб друге завдання не почалося негайно, воно почалося через деякий час через 5 хв та деякий час після 8 хв., І я не знаю, що чи можемо ми контролювати цю поведінку, оскільки це не стандартна поведінка.
користувач1047873

Це звучить як черга завдань підручника.
Звичайний

Так, це просто означає, що всі теми вашого виконавця вже зайняті чимось виконанням, і ваше завдання ставиться в чергу, що потрібно зробити. ( ПРИМІТКА. Вам потрібно підтвердити це, дивлячись на згадану чергу чи дивлячись на те, що виконують потоки виконавця). Як ви це контролюєте, залежить від того, який тип виконавця у вас є. Можливо, ви хочете зробити окремого виконавця з 1 потоком лише для цього завдання, тоді він нічого не чекатиме. Або дайте вашому поточному виконавцю більше тем. Або змінити свою стратегію.
Звичайний

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 |

ПОВІДОМЛЕННЯ час виконання більше, ніж очікування

rasporedWithFixedDelay зберігає
графік затримкиAtFixedRate видаляє затримку


-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
Будь ласка, поясніть, як код вирішує проблему ОП. :)
Яш
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.