Таймер Java против ExecutorService?

263

У меня есть код, где я планирую задачу, используя java.util.Timer. Я огляделся и увидел, что ExecutorServiceможно сделать то же самое. Итак, этот вопрос здесь, вы использовали Timerи ExecutorServiceдля планирования задач, в чем преимущество одного использования над другим?

Также хотел проверить, использовал ли кто-нибудь Timerкласс и столкнулся с какими-либо проблемами, которые ExecutorServiceрешал для них.

кал
источник
1
А если вам нужно что-то еще более функциональное, посмотрите на кварц . Это дает вам гораздо больше контроля над заданиями, включая планирование по принципу cron, планирование с учетом кластеров, индивидуальное управление заданиями (такие понятия, как один запуск за раз, зависимости и т. Д.). --Tim
Tim

Ответы:

313

Согласно Java параллелизма на практике :

  • Timerможет быть чувствительным к изменениям системных часов, ScheduledThreadPoolExecutorне так.
  • Timerимеет только один поток выполнения, поэтому долго выполняющаяся задача может задержать выполнение других задач. ScheduledThreadPoolExecutorможно настроить с любым количеством потоков. Кроме того, вы можете полностью контролировать созданные потоки, если хотите (предоставляя ThreadFactory).
  • Исключения во время выполнения, добавленные в TimerTaskkill этого потока, делают Timerмертвым :-( ... т.е. запланированные задачи больше не будут выполняться. ScheduledThreadExecutorНе только перехватывает исключения времени выполнения, но и позволяет вам обрабатывать их, если хотите (переопределением afterExecuteметода from ThreadPoolExecutor). Задача, которая исключение выбросить будет отменено, но другие задачи будут продолжать выполняться.

Если вы можете использовать ScheduledThreadExecutorвместо Timer, сделайте это.

Еще одна вещь ... хотя ScheduledThreadExecutorона недоступна в библиотеке Java 1.4, есть Backport от JSR 166 ( java.util.concurrent) до Java 1.2, 1.3, 1.4 , который имеет этот ScheduledThreadExecutorкласс.

Петр Штибраны
источник
63

Если это доступно для вас, то трудно придумать причину, по которой не следует использовать среду исполнения Java 5. Вызов:

ScheduledExecutorService ex = Executors.newSingleThreadScheduledExecutor();

даст вам ScheduledExecutorServiceсхожую функциональность Timer(т.е. она будет однопоточной), но доступ к которой может быть немного более масштабируемым (под капотом он использует параллельные структуры, а не полную синхронизацию, как с Timerклассом). Использование ScheduledExecutorServiceтакже дает вам такие преимущества, как:

  • Вы можете настроить его при необходимости (см. newScheduledThreadPoolExecutor()Или ScheduledThreadPoolExecutorкласс)
  • «Одноразовые» казни могут вернуть результаты

Единственные причины, по которым Timerя могу придерживаться :

  • Это доступно до Java 5
  • Подобный класс предусмотрен в J2ME, что может упростить перенос вашего приложения (но в этом случае не составит труда добавить общий уровень абстракции).
Нил Коффи
источник
1
Другая причина использования TimerTaskможет быть доступность scheduledExecutionTime()метода, который, кажется, не имеет никакого аналога в ScheduledExecutorService.
Рохит Агарвал
3
Еще одно примечание: я пишу этот комментарий в 2k17, J2ME больше нет. он уже мертв.
msangel
1
Java Timer-класс дерьмовый.
JohnyTex
26

ExecutorService является более новым и общим. Таймер - это просто поток, который периодически запускает то, что вы запланировали для него.

ExecutorService может быть пулом потоков или даже распространяться на другие системы в кластере и выполнять такие вещи, как одноразовое пакетное выполнение и т. Д.

Просто посмотрите, что каждый предлагает решить.

Dustin
источник
16

Вот еще несколько полезных практик по использованию таймера:

http://tech.puredanger.com/2008/09/22/timer-rules/

В общем, я бы использовал Timer для быстрых и грязных вещей и Executor для более надежного использования.

Алекс Миллер
источник
8

Со страницы документации Oracle на ScheduledThreadPoolExecutor

ThreadPoolExecutor , который дополнительно может запланировать команды для запуска после заданной задержки, или периодически выполнять. Этот класс предпочтительнее, чем Timer, когда требуется несколько рабочих потоков или когда требуется дополнительная гибкость или возможности ThreadPoolExecutor (который расширяет этот класс).

ExecutorService/ThreadPoolExecutorили ScheduledThreadPoolExecutorэто очевидный выбор, когда у вас есть несколько рабочих потоков.

Плюсы ExecutorServiceболееTimer

  1. Timerне могут воспользоваться преимуществами доступных ядер процессора в отличие от ExecutorServiceособенно с несколькими задачами , используя ароматы ExecutorServiceкак ForkJoinPool
  2. ExecutorServiceпредоставляет совместный API, если вам нужна координация между несколькими задачами. Предположим, что вам нужно отправить N рабочих задач и дождаться завершения всех из них. Вы можете легко достичь этого с помощью invokeAll API. Если вы хотите добиться того же с несколькими Timerзадачами, это было бы не просто.
  3. ThreadPoolExecutor предоставляет лучший API для управления жизненным циклом потока.

    Пулы потоков решают две разные проблемы: они обычно обеспечивают повышенную производительность при выполнении большого количества асинхронных задач из-за уменьшения накладных расходов на вызовы для каждой задачи, и они обеспечивают средства ограничения и управления ресурсами, включая потоки, которые используются при выполнении коллекции задачи. Каждый ThreadPoolExecutor также поддерживает некоторую базовую статистику, такую ​​как количество выполненных задач

    Несколько преимуществ:

    а. Вы можете создавать / управлять / контролировать жизненный цикл потоков и оптимизировать накладные расходы на создание потоков

    б. Вы можете контролировать обработку заданий (Work Stealing, ForkJoinPool, invokeAll) и т. Д.

    с. Вы можете отслеживать прогресс и здоровье потоков

    д. Обеспечивает лучший механизм обработки исключений

Равиндра Бабу
источник
5

Моя причина, по которой я иногда предпочитаю 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);

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

Siamaster
источник