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.

Tracz
źródło

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.

Jon Skeet
źródło
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

Ken Block
źródło
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 .

Imar
źródło
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.

shazin
źródło
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

user1047873
źródło
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

Vlad
źródło
-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

logi tech
źródło
1
Proszę również wyjaśnić, w jaki sposób kod rozwiązuje problem OP. :)
Yash