Java库类来处理“回调”的预定执行?

时间:2009-03-05 10:29:46

标签: java design-patterns cron scheduling scheduled-tasks

我的程序有一个组件 - 称为调度程序 - 它允许其他组件及时注册他们想要回调的时间点。这应该像Unix cron服务一样工作,i。即你告诉调度员“每隔一小时过去十分钟就通知我。”

我意识到Java中没有真正的回调。

这是我的方法,是否有一个已经完成这些工作的库?随意提出改进建议。

注册调度程序通行证:

  • 包含小时,分钟,秒,年月,dom,dow的时间规范,其中每个项目可能未指定,意味着“每小时/每分钟执行一次等”。 (就像crontabs一样)
  • 包含数据的对象,该数据将告知调用对象在调度程序通知时要执行的操作。调度程序不处理此数据,只是存储它并在收到通知后将其传回。
  • 对调用对象的引用

在启动时或在新的注册请求之后,Scheduler以当前系统时间的Calendar对象开始,并检查数据库中是否有与此时间点匹配的条目。如果存在,则执行它们并重新开始该过程。如果没有,则Calendar对象中的时间增加一秒,并重新检查entreis。重复这一过程,直到有一个或多个匹配的条目。 (离散事件模拟)

然后,调度程序将记住每秒的时间戳,睡眠和唤醒,以检查它是否已存在。如果它恰好醒来并且时间已经过去,它会重新开始,同样如果时间已到,并且作业已经执行。


编辑:感谢您将我指向Quartz。不过,我正在寻找更小的东西。

8 个答案:

答案 0 :(得分:8)

查找Quartz

答案 1 :(得分:5)

如果您的对象确切地知道他们希望执行的各个时间点,那么您可以使用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);

如果您希望调度程序本身处理“每5秒执行一次”等功能,或者您希望执行错过执行的复杂行为或执行审计跟踪的持久性,则只需使用Quartz

您实际上可以轻松地重复使用Quartz的CronTrigger类来获得“下一个执行时间”。该类完全独立,不依赖于在Quartz“上下文”中调用。将下一个执行时间设为Datelong后,您可以使用上面的Java ScheduledExecutorService

答案 2 :(得分:4)

Quartz是这一领域的一个重要而明显的强国,但还有一些其他选择可以探索。

Cron4j是一个不错的库,比Quartz稍微轻一点。它提供了很好的文档,可以做你想要的。

如果你想使用一个更适合Java并发库(特别是Executors和ScheduledExecutors)的库,可能更有意思的是HA-JDBC有一个CronExecutorService接口,由CronThreadPoolExecutor实现。现在,有趣的是,它依赖于Quartz(提供CronExpression类),但我发现两者结合起来比单独使用Quartz更好。如果你不想要大的依赖,那么很容易从Quartz和HA-JDBC中提取一些类来实现这一点。

因为你想要更小的东西(只是注意到你的编辑),从Quartz中获取CronExpression,以及上面提到的两个HA-JDBC类。那就行了。

答案 3 :(得分:4)

如果您的需求很简单,请考虑使用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();
    }
  }

}

noted一样,ExecutorServicejava.util.concurrent会在您需要时提供更丰富的API。

答案 4 :(得分:1)

我强烈推荐cron4j(已经提到)而不是Quartz,除非你绝对需要Quartz的一些更高级和复杂的功能。 Cron4j非常关注应该做的事情,拥有不错的文档,而且不是厨房解决方案。

答案 5 :(得分:0)

通常建议使用

Quartz scheduler

答案 6 :(得分:0)

无法相信java.util.Timer被选为答案。 Quartz确实是一个更好的选择。

石英相对于java.util.Timer的一大优势是,使用quartz,作业可以存储在db中。因此,一个jvm可以安排,另一个可以执行。 (显然)请求在jvm重启后仍然存在。

答案 7 :(得分:0)

  

如果你想使用一个更适合Java的并发库(特别是Executors和ScheduledExecutors)的库,可能更有趣的是HA-JDBC有一个CronExecutorService接口,由它的CronThreadPoolExecutor实现。现在,有趣的是,它依赖于Quartz(提供CronExpression类),但我发现两者结合起来比单独使用Quartz更好。如果你不想要大的依赖,那么很容易从Quartz和HA-JDBC中提取一些类来实现这一点。

我只是想说我尝试提取这些类,并且它有效!我需要这三个类:

  • CronExpression(quartz)
  • CronThreadPoolExecutor(ha-jdbc)
  • DaemonThreadFactory(ha-jdbc)

我只需做一些小调整:

  • 从CronThreadPoolExecutor中删除记录器(它已创建但从未使用过)
  • 将常量YEAR_TO_GIVEUP_SCHEDULING_AT从CronTrigger移至CronExpression

我很高兴我没有被困在依赖的纠结中。恭喜班级作者!

它一直像冠军一样工作。