Класс библиотеки Java для обработки запланированного выполнения «обратных вызовов»? - PullRequest
7 голосов
/ 05 марта 2009

Моя программа имеет компонент, называемый планировщиком, который позволяет другим компонентам регистрировать моменты времени, в которые они хотят получить обратный вызов. Это должно работать так же, как служба Unix cron, т.е. е. Вы говорите Планировщику: «Сообщите мне через десять минут после каждого полного часа».

Я понимаю, что в Java нет реальных обратных вызовов.

Вот мой подход, есть ли библиотека, которая уже занимается этим? Не стесняйтесь предлагать улучшения тоже.

Регистрация звонка на планировщик пропусков:

  • указание времени, содержащее часы, минуты, секунды, месяц года, dom, dow, где каждый элемент может быть не указан, что означает «выполнять его каждый час / минуту и ​​т. Д.» (так же, как crontabs)
  • объект, содержащий данные, которые сообщают вызывающему объекту, что делать, когда он уведомлен планировщиком. Планировщик не обрабатывает эти данные, просто сохраняет их и передает обратно после уведомления.
  • ссылка на вызывающий объект

При запуске или после нового запроса на регистрацию Планировщик запускается с объектом Calendar текущего системного времени и проверяет, есть ли какие-либо записи в базе данных, которые соответствуют этому моменту времени. Если они есть, они выполняются, и процесс начинается заново. Если нет, время в объекте Calendar увеличивается на одну секунду, и entreis перепроверяются. Это повторяется до тех пор, пока не будет найдена одна или несколько записей. (Моделирование дискретных событий)

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


Редактировать : Спасибо, что указали мне на Кварц. Однако я ищу что-то гораздо меньшее.

Ответы [ 8 ]

8 голосов
/ 05 марта 2009

Поиск Кварц

5 голосов
/ 05 марта 2009

Если ваши объекты точно знают отдельные моменты времени, которые они хотят выполнить, то вы можете использовать java.util.concurrent.ScheduledExecutorService. Тогда они просто называют:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
long timeToExecute = ... //read from DB? use CronTrigger?
long delayToExecution = timeToExecute - System.currentTimeMillis();
scheduler.schedule(aRunnable, delayToExecution, TimeUnit.MILLISECONDS);

Вам нужно будет использовать Quartz только в том случае, если вы хотите, чтобы сам планировщик обрабатывал такие функции, как «выполнять каждые 5 секунд», или если вы хотите, чтобы сложное поведение было связано с пропущенными выполнениями или сохранением журнала аудита выполнения. 1006 *

На самом деле вы можете тривиально использовать класс CronTrigger в Quartz, чтобы получить «следующий раз выполнения». Класс полностью автономен и не зависит от того, вызывается ли он из «контекста» Quartz. Если у вас есть следующий раз выполнения как Date или long, вы можете просто использовать Java ScheduledExecutorService, как указано выше

4 голосов
/ 05 марта 2009

Если ваши потребности просты, рассмотрите возможность использования java.util.Timer :

public class TimerDemo {

  public static void main(String[] args) {
    // non-daemon threads prevent termination of VM
    final boolean isDaemon = false;
    Timer timer = new Timer(isDaemon);

    final long threeSeconds = 3 * 1000;
    final long delay = 0;
    timer.schedule(new HelloTask(), delay, threeSeconds);

    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.MINUTE, 1);
    Date oneMinuteFromNow = calendar.getTime();

    timer.schedule(new KillTask(timer), oneMinuteFromNow);
  }

  static class HelloTask extends TimerTask {
    @Override
    public void run() {
      System.out.println("Hello");
    }
  }

  static class KillTask extends TimerTask {

    private final Timer timer;

    public KillTask(Timer timer) {
      this.timer = timer;
    }

    @Override
    public void run() {
      System.out.println("Cancelling timer");
      timer.cancel();
    }
  }

}

Как было отмечено , ExecutorService из java.util.concurrent предлагает более богатый API, если вам это нужно.

4 голосов
/ 05 марта 2009

Кварц - большая и очевидная электростанция в этой области, но есть некоторые альтернативы для изучения.

Cron4j - достаточно приличная библиотека, которая немного легче, чем Quartz. Он предоставляет хорошую документацию и будет делать то, что вы хотите.

Вероятно, более интересно, если вы хотите использовать библиотеку, которая лучше подходит для библиотек параллелизма Java (особенно Executors и ScheduledExecutors), тогда HA-JDBC имеет интерфейс CronExecutorService , реализованный его CronThreadPoolExecutor . Интересно, что у него есть зависимость от Quartz (для предоставления класса CronExpression), но я обнаружил, что они вместе работают лучше, чем один только Quartz. Если вам не нужны большие зависимости, легко извлечь несколько классов из Quartz и HA-JDBC, которые делают это возможным.

Поскольку вам нужно что-то намного меньшее (только что заметил ваше редактирование), возьмите CronExpression из Quartz и два класса HA-JDBC, которые я упомянул выше. Это сделает это.

1 голос
/ 07 апреля 2010

Я бы настоятельно рекомендовал cron4j (уже упоминалось) поверх Quartz, если только вам абсолютно не нужны некоторые более сложные и сложные функции Quartz. Cron4j прекрасно фокусируется на том, что он должен делать, имеет приличную документацию и не является решением для кухонной мойки.

0 голосов
/ 11 февраля 2010

Возможно, более интересно, если вы хотите использовать библиотеку, которая лучше подходит для библиотек параллелизма Java (особенно Executors и ScheduledExecutors), тогда HA-JDBC имеет интерфейс CronExecutorService, реализованный его CronThreadPoolExecutor. Интересно, что у него есть зависимость от Quartz (для предоставления класса CronExpression), но я обнаружил, что они вместе работают лучше, чем один только Quartz. Если вам не нужны большие зависимости, легко извлечь несколько классов из Quartz и HA-JDBC, которые делают это возможным.

Я просто хотел сказать, что я пытался извлечь эти классы, и это сработало! Мне были нужны эти три класса:

  • CronExpression (кварц)
  • CronThreadPoolExecutor (ha-jdbc)
  • DaemonThreadFactory (ha-jdbc)

И мне нужно было сделать только эти незначительные изменения:

  • Удаление регистратора из CronThreadPoolExecutor (он был создан, но никогда не использовался)
  • Переместил константу YEAR_TO_GIVEUP_SCHEDULING_AT из CronTrigger в CronExpression

Я был взволнован тем, что не застрял в путанице зависимостей. Поздравляем авторов класса!

И он работал как чемпион.

0 голосов
/ 07 марта 2009

Не могу поверить, java.util.Timer был выбран в качестве ответа. Кварц - действительно лучший выбор.

Большое преимущество кварца перед java.util.Timer заключается в том, что с кварцем задания могут храниться в БД. В результате один jvm может запланировать, а другой может выполнить. Также (очевидно) запрос сохраняется при перезапусках jvm.

0 голосов
/ 05 марта 2009
...