scheduleAtFixedRate a scheduleWithFixedDelay


118

Jaka jest główna różnica między scheduleAtFixedRatei scheduleWithFixedDelaymetodami usługi 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);

drukują dokładnie w tym samym czasie, wydaje się, że są wykonywane dokładnie w tym samym czasie.

Odpowiedzi:


207

Spróbuj dodać Thread.sleep(1000);wezwanie w swojej run()metody ... W zasadzie to jest różnica między szeregowania coś na podstawie Kiedy poprzednich realizacji celów i kiedy (logicznie) zaczyna .

Na przykład, przypuśćmy, że planuję uruchamianie alarmu ze stałą częstotliwością raz na godzinę i za każdym razem, gdy się włącza, piję kawę, co zajmuje 10 minut. Załóżmy, że zaczyna się o północy, miałbym:

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

Gdybym zaplanował ze stałym godzinnym opóźnieniem , miałbym:

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

Który z nich chcesz, zależy od twojego zadania.


18
Co się dzieje w scenariuszu fixedRate, jeśli przygotowanie kawy zajmuje więcej niż godzinę?
— Brett VanderVeen

5
@BrettVanderVeen: Uważam, że to zależy od danego wykonawcy. Zostanie zaplanowany na czas - ale to, czy zostanie wykonany, zależy od tego, czy wątek jest dostępny dla tego modułu wykonawczego. Proponuję poeksperymentować, aby zobaczyć, jak to działa w różnych scenariuszach.
— Jon Skeet,

8
@BrettVanderVeen Z dokumentacji : „Jeśli wykonanie tego zadania trwa dłużej niż jego okres, kolejne mogą rozpocząć się z opóźnieniem, ale nie będą wykonywane równolegle”. Innymi słowy, zgodna implementacja nie pozwoli na wykonanie następnej, dopóki poprzednia nie zostanie zakończona.
— M. Justin,

Czy możesz podać działający kod dla pokazanego wyjścia (kawa) dla nowicjusza takiego jak ja?
— MuneshSingh

@MuneshSingh: Nie w tym pytaniu, które prosi o wyjaśnienie, jaka jest różnica między planowaniem ze stałą stawką a harmonogramem ze stałym opóźnieniem. I tak nie zaimplementowałbyś tego samodzielnie - używałbyś wbudowanych executorów.
— Jon Skeet,

57

Wizualizuj szeregi czasowe scheduleAtFixedRatemetody wywołania . Kolejne egzekucje rozpoczną się natychmiast, jeśli ostatnia potrwa dłużej niż okres. W przeciwnym razie rozpocznie się po upływie określonego czasu.

szereg czasowy wywołania metody scheduleAtFixedRate

Szeregi czasowe scheduleWithFixedDelaymetody wywołania . Kolejna realizacja rozpocznie się po czasie opóźnienia między zakończeniem jednej realizacji a rozpoczęciem następnej, niezależnie od czasu jej wykonania

szereg czasowy wywołania scheduleWithFixedDelay metody

Hope może ci pomóc


Nie mogłem zrozumieć „dodatkowego” słowa wymienionego na diagramie szeregów czasowych scheduleAtFixedRate.
— MuneshSingh

1
@MuneshSingh Ma to na celu pokazanie, że czas wykonania zadania jest dłuższy niż zaplanowano, więc zajmuje to „dodatkowy” czas i następne wykonanie rozpoczyna się od razu.
— Viorel

@ Viorel dzięki za wyjaśnienie. Czy to oznacza, że ​​„okres” nie jest dokładnie ustalonym opóźnieniem czasowym między dwoma kolejnymi wykonaniami.
— MuneshSingh

1
@MuneshSingh Okres jest ustalony, ale nie zatrzyma bieżącego zadania po jego przejściu, po prostu nie będzie opóźnienia między tym uruchomieniem a następnym. Jeśli chcesz utworzyć "limit czasu", możesz chcieć zachować przyszłość i anulować ją w innym executorze. Mówi się w prostych słowach rozpocznij pierwszą realizację, a następną tak szybko, jak to możliwe po upływie „okresu” .
— Viorel

4

scheduleAtFixedRate()Metoda tworzy nowe zadanie i przekazuje go do realizatora każdy okres, niezależnie od tego, czy poprzednie zadanie zakończone .

Z drugiej strony scheduleWithFixedDelay()metoda tworzy nowe zadanie po zakończeniu poprzedniego zadania .


Napisałeś dwa razy scheduleAtFixedRate:)
— Vlad

3

Jeśli przeczytasz dokument Java, będzie to jaśniejsze

ScheduledFuture scheduleAtFixedRate (polecenie Runnable, long initialDelay, long period, timeUnit) Tworzy i wykonuje okresowe działanie, które jest włączane najpierw po zadanym początkowym opóźnieniu, a następnie w zadanym okresie; to znaczy, że wykonanie rozpocznie się po initialDelay, a następnie initialDelay + okres, a następnie initialDelay + 2 * okres i tak dalej.

ScheduledFuture scheduleWithFixedDelay (Runnable command, long initialDelay, long delay, TimeUnit unit) Tworzy i wykonuje okresowe działanie, które jest włączane najpierw po zadanym początkowym opóźnieniu, a następnie z zadanym opóźnieniem między zakończeniem jednego wykonania a rozpoczęciem następnego.


1

Jest jeden haczyk w scheduleAtFixedRate, jeśli pierwszy wątek trwa zbyt długo i nie został zakończony w podanym czasie, drugi kolejny wątek nie rozpocznie się po zakończeniu pierwszego zadania i nie rozpocznie się natychmiast, gdy pierwszy wątek zakończy swoje zadanie i uzyska czas trwania upłynął. JVM Zdecyduje, kiedy zostanie wykonane następne zadanie.

Myślę, że pomoże ci to wybrać metodę, ponieważ z tego powodu mam duży problem


1
Co? JVM zdecyduje? Co to w ogóle ma znaczyć? Prawdą jest, że plik runnable nie zostanie wykonany równolegle ze sobą zgodnie z dokumentacją, ale decyduje o tym executor, który może być niestandardowy LUB standardowy ScheduledThreadPoolExecutor(a ten ostatni ma dobrze zdefiniowane zachowanie)
— Zwykły

Nie, znalazłem podobny problem w mojej aplikacji, gdzie dałem 15-minutową przerwę, a pierwsze zadanie nie kończyło się w 15 minut i trwało 15.30 sekund, więc drugie zadanie nie zostało uruchomione od razu, zaczęło się je po 5 minutach i jakiś czas później 8 min i nie wiem, czy możemy kontrolować takie zachowanie, ponieważ nie jest to standardowe zachowanie.
— user1047873,

To brzmi jak kolejkowanie zadań z podręczników.
— Ordous

Tak, to po prostu oznacza, że ​​wszystkie wątki w twoim executorze są już zajęte robieniem czegoś, a twoje zadanie zostaje umieszczone w kolejce rzeczy do zrobienia. ( UWAGA , musisz to potwierdzić, patrząc na wspomnianą kolejkę lub patrząc na to, co robią wątki wykonawcy). To, w jaki sposób to kontrolujesz, zależy od typu posiadanego executora. Możesz chcieć utworzyć oddzielny 1-wątkowy moduł wykonawczy tylko dla tego konkretnego zadania, wtedy nie będzie on na nic czekał. Lub daj swojemu obecnemu executorowi więcej wątków. Albo zmień jego strategię.
— Ordous

0

Napiszmy prosty program:

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)

I zobacz wyniki:

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

UWAGA czas wykonania jest dłuższy niż czekanie

scheduleWithFixedDelay utrzymuje opóźnienie
scheduleAtFixedRate usuwa opóźnienie


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

Po prostu wykonaj to, a poznasz różnicę. Dziękuję Ci


1
Proszę również wyjaśnić, w jaki sposób kod rozwiązuje problem OP. :)
— Yash
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.