Java Timer vs ExecutorService?
У меня есть код, где я планирую задачу, используя java.util.Timer
. Я огляделся и увидел, что ExecutorService
может сделать то же самое. Итак, этот вопрос здесь: использовали ли вы Timer
и ExecutorService
для планирования задач, в чем преимущество одного использования над другим?
Также хотел проверить, использовал ли кто-нибудь класс Timer
и сталкивался ли с какими-либо проблемами, которые ExecutorService
решил для них.
Ответы
Ответ 1
Согласно Java Concurrency на практике:
-
Timer
может быть чувствительным к изменениям в системных часах, ScheduledThreadPoolExecutor
- нет.
-
Timer
имеет только один поток выполнения, поэтому длительная задача может задержать другие задачи. ScheduledThreadPoolExecutor
можно настроить с любым количеством потоков. Кроме того, вы имеете полный контроль над созданными потоками, если хотите (путем предоставления ThreadFactory
).
- Исключения, выполняемые в
TimerTask
, убивают один поток, что делает Timer
dead:-(... т.е. запланированные задачи больше не будут выполняться. ScheduledThreadExecutor
не только улавливает исключения во время выполнения, но и позволяет обрабатывать их если вы хотите (путем переопределения метода afterExecute
из ThreadPoolExecutor
). Задача, которая выбрала исключение, будет отменена, но другие задачи будут продолжать выполняться.
Если вы можете использовать ScheduledThreadExecutor
вместо Timer
, сделайте это.
Еще одна вещь... пока ScheduledThreadExecutor
недоступна в библиотеке Java 1.4, есть Backport of JSR 166 (java.util.concurrent
) для Java 1.2, 1.3, 1.4, который имеет класс ScheduledThreadExecutor
.
Ответ 2
Если это доступно вам, тогда трудно подумать о причине не использовать структуру исполнителей Java 5. Призвание:
ScheduledExecutorService ex = Executors.newSingleThreadScheduledExecutor();
предоставит вам ScheduledExecutorService
с аналогичной функциональностью Timer
(то есть он будет однопоточным), но доступ которого может быть немного более масштабируемым (под капотом он использует параллельные структуры, а не полную синхронизацию, как с Timer
). Использование ScheduledExecutorService
также дает вам следующие преимущества:
- Вы можете настроить его, если это необходимо (см. класс
newScheduledThreadPoolExecutor()
или ScheduledThreadPoolExecutor
)
- Выполнение "одного выключения" может возвращать результаты
Об единственных причинах придерживаться Timer
, о которых я могу думать:
- Доступна предварительная Java 5
- Аналогичный класс предоставляется в J2ME, что упрощает перенос приложения (но в этом случае было бы сложно добавить общий уровень абстракции)
Ответ 3
ExecutorService является более новым и более общим. Таймер - это просто поток, который периодически запускает материалы, которые вы запланировали для него.
ExecutorService может быть пулом потоков или даже распространяться по другим системам в кластере и делать что-то вроде одноразового пакетного исполнения и т.д.
Просто посмотрите, что каждый из них решит решить.
Ответ 4
Вот еще несколько примеров использования таймера:
http://tech.puredanger.com/2008/09/22/timer-rules/
В общем, я бы использовал таймер для быстрого и грязного материала и Executor для более надежного использования.
Ответ 5
На странице документации Oracle на ScheduledThreadPoolExecutor
ThreadPoolExecutor, который может дополнительно планировать выполнение команд после заданной задержки или выполнять их периодически. Этот класс является предпочтительным для Таймера, когда необходимы несколько рабочих потоков или когда требуется дополнительная гибкость или возможности ThreadPoolExecutor (который распространяется по этому классу).
ExecutorService/ThreadPoolExecutor
или ScheduledThreadPoolExecutor
- это очевидный выбор, если у вас есть несколько рабочих потоков.
Плюсы ExecutorService
по Timer
-
Timer
не может использовать имеющиеся ядра ЦП в отличие от ExecutorService
особенно с несколькими задачами, используя ароматы ExecutorService
такие как ForkJoinPool -
ExecutorService
предоставляет API совместной работы, если вам нужна координация между несколькими задачами. Предположим, что вам нужно отправить N количество рабочих заданий и дождаться завершения их работы. Вы можете легко достичь этого с помощью API invokeAll. Если вы хотите достичь того же с несколькими задачами Timer
, это будет непросто. -
ThreadPoolExecutor предоставляет лучший API для управления жизненным циклом потока.
Пулы потоков адресуют две разные проблемы: они обычно обеспечивают улучшенную производительность при выполнении большого количества асинхронных задач из-за сокращения служебных издержек на одну задачу и обеспечивают средства для ограничения и управления ресурсами, включая потоки, потребляемые при выполнении коллекции задания. Каждый ThreadPoolExecutor также поддерживает некоторые базовые статистические данные, такие как количество завершенных задач
Немногие преимущества:
а. Вы можете создавать/управлять/контролировать жизненный цикл потоков и оптимизировать накладные расходы на создание потоков
б. Вы можете контролировать обработку заданий ("Работа кражи", "ForkJoinPool", "invokeAll") и т.д.
с. Вы можете следить за ходом и здоровьем потоков
д. Обеспечивает лучший механизм обработки исключений
Ответ 6
Моя причина иногда предпочитает Timer над Executors.newSingleThreadScheduledExecutor() заключается в том, что я получаю намного более чистый код, когда мне нужно, чтобы таймер выполнял потоки демона.
сравнить
private final ThreadFactory threadFactory = new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}
};
private final ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor(threadFactory);
с
private final Timer timer = new Timer(true);
Я делаю это, когда мне не нужна надежность службы-исполнителя.