Timer & TimerTask против Thread + sleep в Java

102

Я нашел здесь похожие вопросы, но не получил удовлетворительных ответов. Итак, перефразируя вопрос еще раз -

У меня есть задача, которую нужно выполнять периодически (скажем, с интервалом в 1 минуту). В чем преимущество использования Timertask и Timer для этого по сравнению с созданием нового потока, который имеет бесконечный цикл со сном?

Фрагмент кода с использованием timertask-

TimerTask uploadCheckerTimerTask = new TimerTask(){

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

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

Фрагмент кода с использованием Thread и sleep-

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

Мне действительно не нужно беспокоиться, если я пропущу определенные циклы, если выполнение логики занимает больше времени, чем интервал.

Прокомментируйте, пожалуйста ..

Обновление:
недавно я обнаружил еще одно различие между использованием Timer и Thread.sleep (). Предположим, что текущее системное время - 11:00. Если по какой-то причине мы откатим системное время до 10:00, таймер прекратит выполнение задачи, пока не достигнет 11:00, тогда как метод Thread.sleep () продолжит выполнение задачи без помех. Это может быть важным фактором при принятии решения о том, что использовать между этими двумя.

Кешав
источник
21
Мнение по порядку ведения: Timer и TimerTask устарели и были фактически заменены ExecutorService, хотя ваша точка зрения все еще остается в силе.
skaffman
Спасибо за подсказку, я решил использовать ExecutorService :)
Кешав
Спасибо всем за ответы, конечно, помогли мне понять!
Кешав
6
Таймер не устарел и предпочтительнее, когда нужен только один поток. ( java.sun.com/javase/6/docs/api/java/util/Timer.html )
Джастин
2
Timer и TimerTask по-прежнему полезны в средах JME, где ExecutorService не существует (начиная с JME Java 1.3 на основе ...).
ス ー パ ー フ ァ ミ コ ン

Ответы:

67

Преимущество TimerTask состоит в том, что он намного лучше выражает ваше намерение (т.е. читаемость кода), и в нем уже реализована функция cancel ().

Обратите внимание, что это может быть написано в более короткой форме, а также в вашем собственном примере:

Timer uploadCheckerTimer = new Timer(true);
uploadCheckerTimer.scheduleAtFixedRate(
    new TimerTask() {
      public void run() { NewUploadServer.getInstance().checkAndUploadFiles(); }
    }, 0, 60 * 1000);
Зед
источник
если таймер используется для повседневного использования, в 2 конкретных времени, 21:00 и 9:00, ... как указать значения? в коде выше ... @Zed?
gumuruh
12

Timer / TimerTask также учитывает время выполнения вашей задачи, поэтому оно будет немного точнее. И он лучше справляется с проблемами многопоточности (такими как предотвращение взаимоблокировок и т. Д.). И, конечно, обычно лучше использовать проверенный стандартный код вместо какого-то самодельного решения.

MartinStettner
источник
5

Я не знаю почему, но программа, которую я писал, использовала таймеры, и размер кучи постоянно увеличивался, как только я изменил его на проблему с потоком / сном, решена.

Qandeel
источник
9
Таймер создает очередь задач, которая постоянно обновляется. Когда таймер завершен, он не может быть немедленно собран. Таким образом, создание большего количества таймеров только добавляет больше объектов в кучу. Thread.sleep () только приостанавливает поток, поэтому накладные расходы на память будут чрезвычайно низкими.
Дэррил Герроу,
4

Если вы получаете исключение и убиваете поток, это проблема. Но TimerTask позаботится об этом. Он будет работать независимо от сбоя в предыдущем запуске.

Стек Поппер
источник
4

Из Timer документации :

В Java 5.0 представлен пакет java.util.concurrent, и одна из утилит параллелизма в нем - ScheduledThreadPoolExecutor, который представляет собой пул потоков для многократного выполнения задач с заданной скоростью или задержкой. По сути, это более универсальная замена комбинации Timer / TimerTask, поскольку она позволяет использовать несколько потоков службы, принимает различные единицы времени и не требует создания подклассов TimerTask (просто реализуйте Runnable). Настройка ScheduledThreadPoolExecutor с одним потоком делает его эквивалентным Timer.

Так что предпочитайте ScheduledThreadExecutorвместо Timer:

  • Timerиспользует один фоновый поток, который используется для последовательного выполнения всех задач таймера. Таким образом, задачи должны выполняться быстро, иначе это приведет к задержке выполнения последующих задач. Но в случае, если ScheduledThreadPoolExecutorмы можем настроить любое количество потоков, а также можем иметь полный контроль, предоставив ThreadFactory.
  • Timerможет быть чувствительным к системным часам, поскольку использует Object.wait(long)метод. Но ScheduledThreadPoolExecutorнет.
  • Исключения времени выполнения, созданные в TimerTask, уничтожат этот конкретный поток, тем самым сделав Timer мертвым там, где мы можем обработать это, ScheduledThreadPoolExecutorчтобы не затронуть другие задачи.
  • Timerпредоставляет cancelметод для завершения таймера и отмены любых запланированных задач, однако он не мешает текущей выполняемой задаче и позволяет ей завершиться. Но если таймер работает как поток демона, то, отменяем мы его или нет, он завершится, как только все пользовательские потоки закончат выполнение.

Таймер против Thread.sleep

Таймер использует, Object.waitи он отличается отThread.sleep

  1. Поток ожидания ( wait) может быть уведомлен (использован notify) другим потоком, но спящий не может быть, его можно только прервать.
  2. Ожидание (и уведомление) должно происходить в блоке, синхронизированном с объектом монитора, тогда как спящий режим - нет.
  3. Пока сон не снимает блокировку, ожидание снимает блокировку для объекта, вызываемого ожиданием.
akhil_mittal
источник
очень полезная информация. Плюс использование Thread.sleep в бесконечном цикле может вызвать высокую загрузку ЦП при небольших задержках по времени.
Амир Фо
3

Есть один важный аргумент против управления этой задачей с помощью потоков и sleepметодов Java . Вы используете, while(true)чтобы оставаться в цикле на неопределенное время и перевести поток в спящий режим. Что, если NewUploadServer.getInstance().checkAndUploadFiles();потребует синхронизированных ресурсов. Другие потоки не смогут получить доступ к этим ресурсам, может произойти голодание, которое может замедлить работу всего приложения. Ошибки такого типа сложно диагностировать, и рекомендуется предотвратить их появление.

Другой aproach запускает выполнение кода, который важен для вас, то есть NewUploadServer.getInstance().checkAndUploadFiles();путем вызова run()вашего метода TimerTask, позволяя тем временем другим потокам использовать ресурсы.

Борис Павлович
источник
16
Я не понимаю этого аргумента. Оба варианта запускают один и тот же метод из потока, оба варианта находятся в режиме ожидания в потоке, ожидая выполнения. Между двумя вариантами не будет разницы в голоде.
satur9nine
2

Я думаю, что понимаю вашу проблему, я вижу нечто очень похожее. У меня есть таймеры, которые повторяются, некоторые каждые 30 минут, а некоторые каждые пару дней. Из того, что я читал, и из комментариев, которые я вижу, похоже, что сборка мусора никогда не будет запущена, потому что все задачи никогда не завершаются. Я бы подумал, что сборка мусора будет запускаться, когда таймер находится в спящем режиме, но я этого не вижу, и, согласно документации, этого не происходит.

Я думаю, что создание новых потоков завершено и разрешена сборка мусора.

Кто-нибудь, пожалуйста, докажите, что я неправ, переписывать то, что я унаследовал, будет болью.

Кен
источник