Timer & TimerTask a Thread + Sleep w Javie

102

Znalazłem tutaj podobne pytania, ale nie znalazłem odpowiedzi w sposób satysfakcjonujący. Więc ponownie sformułuj pytanie

Mam zadanie, które należy wykonywać okresowo (powiedzmy w odstępach 1-minutowych). Jaka jest zaleta korzystania z funkcji Timertask i Timer, w przeciwieństwie do tworzenia nowego wątku, który ma nieskończoną pętlę ze snem?

Fragment kodu za pomocą timertask-

TimerTask uploadCheckerTimerTask = new TimerTask(){

 public void run() {
  NewUploadServer.getInstance().checkAndUploadFiles();
 }
};

Timer uploadCheckerTimer = new Timer(true);
uploadCheckerTimer.scheduleAtFixedRate(uploadCheckerTimerTask, 0, 60 * 1000);

Fragment kodu używający wątku i uśpienia

Thread t = new Thread(){
 public void run() {
  while(true) {
   NewUploadServer.getInstance().checkAndUploadFiles();
   Thread.sleep(60 * 1000);
  }
 }
};
t.start();

Naprawdę nie muszę się martwić, jeśli przegapię pewne cykle, jeśli wykonanie logiki zajmuje więcej niż czas interwału.

Proszę o komentarz na ten temat ...

Aktualizacja:
Ostatnio znalazłem kolejną różnicę między używaniem Timera a Thread.sleep (). Załóżmy, że aktualny czas systemowy to 11:00. Jeśli z jakiegoś powodu cofniemy czas systemowy do godziny 10:00, Timer ZATRZYMUJE wykonywanie zadania do godziny 11:00, podczas gdy metoda Thread.sleep () będzie kontynuować wykonywanie zadania bez przeszkód. Może to być główny decydent przy podejmowaniu decyzji, co użyć między tymi dwoma.

Keshav
źródło
21
Punkt zamówienia: Timer i TimerTask są przestarzałe i zostały skutecznie zastąpione przez ExecutorService, chociaż Twój punkt widzenia jest nadal aktualny.
skaffman
Dzięki za wskazówkę zdecydowałem się skorzystać z ExecutorService :)
Keshav
Dziękuję wszystkim za odpowiedzi, na pewno dały mi więcej zrozumienia!
Keshav
6
Licznik czasu nie jest przestarzały i jest preferowany, gdy potrzebny jest tylko jeden wątek. ( java.sun.com/javase/6/docs/api/java/util/Timer.html )
Justin
2
Timer i TimerTask są nadal przydatne w środowiskach JME, w których nie istnieje usługa ExecutorService (od JME Java 1.3 ...).
ス ー パ ー フ ァ ミ コ ン

Odpowiedzi:

67

Zaletą TimerTask jest to, że znacznie lepiej wyraża on Twój zamiar (tj. Czytelność kodu) i ma już zaimplementowaną funkcję cancel ().

Zwróć uwagę, że można go napisać w krótszej formie, jak również własny przykład:

Timer uploadCheckerTimer = new Timer(true);
uploadCheckerTimer.scheduleAtFixedRate(
    new TimerTask() {
      public void run() { NewUploadServer.getInstance().checkAndUploadFiles(); }
    }, 0, 60 * 1000);
Zed
źródło
jeśli timer jest używany codziennie, o 2 określonych godzinach 21:00 i 9:00, ... jak podać wartości? na powyższym kodzie ... @Zed?
gumuruh
12

Timer / TimerTask bierze również pod uwagę czas wykonania twojego zadania, więc będzie trochę dokładniejszy. I lepiej radzi sobie z problemami wielowątkowymi (takimi jak unikanie zakleszczeń itp.). I oczywiście zwykle lepiej jest użyć dobrze przetestowanego standardowego kodu zamiast jakiegoś domowego rozwiązania.

MartinStettner
źródło
5

Nie wiem dlaczego, ale program, który pisałem, używał timerów, a jego rozmiar sterty stale się zwiększał, gdy zmieniłem go na rozwiązany problem wątku / snu.

Qandeel
źródło
9
Timer tworzy kolejkę zadań, która jest stale aktualizowana. Gdy Timer jest gotowy, może nie zostać natychmiast zebrany jako śmieci. Zatem tworzenie większej liczby timerów dodaje tylko więcej obiektów do stosu. Thread.sleep () tylko wstrzymuje wątek, więc narzut pamięci byłby wyjątkowo niski.
Darryl Gerrow
4

Jeśli wątek dostanie wyjątek i zostanie zabity, to jest problem. Ale TimerTask zajmie się tym. Będzie działać niezależnie od awarii w poprzednim przebiegu.

Stack Popper
źródło
4

Z Timer dokumentacji :

Java 5.0 wprowadziła pakiet java.util.concurrent, a jednym z narzędzi współbieżności w nim zawartych jest ScheduledThreadPoolExecutor, który jest pulą wątków do wielokrotnego wykonywania zadań z określoną szybkością lub opóźnieniem. Skutecznie jest bardziej wszechstronnym zamiennikiem kombinacji Timer / TimerTask, ponieważ pozwala na wiele wątków usług, akceptuje różne jednostki czasu i nie wymaga podklasy TimerTask (po prostu zaimplementuj Runnable). Skonfigurowanie ScheduledThreadPoolExecutor z jednym wątkiem powoduje, że jest to odpowiednik Timer.

Więc wolę ScheduledThreadExecutorzamiast Timer:

  • Timerużywa pojedynczego wątku w tle, który jest używany do sekwencyjnego wykonywania wszystkich zadań timera. Zadania powinny więc zakończyć się szybko, w przeciwnym razie opóźni to wykonanie kolejnych zadań. Ale w tym przypadku ScheduledThreadPoolExecutormożemy skonfigurować dowolną liczbę wątków, a także mieć pełną kontrolę, udostępniając ThreadFactory.
  • Timermoże być wrażliwy na zegar systemowy, ponieważ korzysta z Object.wait(long)metody. Ale ScheduledThreadPoolExecutortak nie jest.
  • Wyjątki środowiska uruchomieniowego zgłoszone w TimerTask zabiją ten konkretny wątek, co spowoduje, że Timer będzie martwy, ponieważ możemy to obsłużyć, ScheduledThreadPoolExecutoraby nie wpływać na inne zadania.
  • Timerzapewnia cancelmetodę kończenia licznika czasu i odrzucania zaplanowanych zadań, jednak nie koliduje z aktualnie wykonywanym zadaniem i nie pozwala na jego zakończenie. Ale jeśli zegar działa jako wątek demona, to niezależnie od tego, czy go anulujemy, czy nie, zakończy się, gdy tylko wszystkie wątki użytkownika zostaną zakończone.

Timer vs Thread.sleep

Timer korzysta z Object.waiti różni się odThread.sleep

  1. Oczekujący ( wait) wątek może zostać powiadomiony (za pomocą notify) przez inny wątek, ale śpiącego nie można, można go tylko przerwać.
  2. Czekanie (i powiadamianie) musi nastąpić w bloku zsynchronizowanym z obiektem monitora, podczas gdy tryb uśpienia nie.
  3. Podczas gdy sen nie zwalnia blokady, czekanie zwalnia blokadę na wywołanie czekania obiektu.
akhil_mittal
źródło
bardzo przydatne informacje, a ponadto używanie Thread.sleep w nieskończonej pętli może powodować wysokie zużycie procesora przy niewielkich opóźnieniach czasowych.
Amir Data
3

Jest jeden kluczowy argument przeciwko zarządzaniu tym zadaniem za pomocą wątków i sleepmetody Java . Używasz while(true)do pozostawania w pętli na czas nieokreślony i hibernacji wątku poprzez uśpienie. A jeśli NewUploadServer.getInstance().checkAndUploadFiles();zajmie trochę zsynchronizowanych zasobów. Inne wątki nie będą miały dostępu do tych zasobów, może zdarzyć się głód, który może spowolnić całą aplikację. Tego typu błędy są trudne do zdiagnozowania i dobrze jest zapobiegać ich występowaniu.

Drugie podejście wyzwala wykonanie kodu, który jest dla Ciebie ważny, tj. Poprzez NewUploadServer.getInstance().checkAndUploadFiles();wywołanie swojej run()metody TimerTask, pozwalając w międzyczasie innym wątkom korzystać z zasobów.

Boris Pavlović
źródło
16
Nie rozumiem tego argumentu. Obie opcje uruchamiają tę samą metodę z wątku, obie opcje śpią w wątku podczas oczekiwania na wykonanie. Nie będzie różnic w głodzie między tymi dwoma wyborami.
satur9nine
2

Myślę, że rozumiem Twój problem, widzę coś bardzo podobnego. Mam cykliczne liczniki czasu, niektóre co 30 minut, a niektóre co kilka dni. Z tego, co przeczytałem i z komentarzy, które widzę, wygląda na to, że zbieranie śmieci nigdy nie zostanie uruchomione, ponieważ wszystkie zadania nigdy nie są zakończone. Pomyślałbym, że zbieranie śmieci będzie działać, gdy zegar jest w stanie uśpienia, ale ja go nie widzę i zgodnie z dokumentacją nie.

Myślę, że tworzenie nowych wątków kończy się i pozwala na zbieranie śmieci.

Niech ktoś udowodni, że się mylę, przepisywanie tego, co odziedziczyłem, będzie bolało.

Rozpoznać
źródło