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

  1. Timer не может использовать имеющиеся ядра ЦП в отличие от ExecutorService особенно с несколькими задачами, используя ароматы ExecutorService такие как ForkJoinPool
  2. ExecutorService предоставляет API совместной работы, если вам нужна координация между несколькими задачами. Предположим, что вам нужно отправить N количество рабочих заданий и дождаться завершения их работы. Вы можете легко достичь этого с помощью API invokeAll. Если вы хотите достичь того же с несколькими задачами Timer, это будет непросто.
  3. 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);

Я делаю это, когда мне не нужна надежность службы-исполнителя.