7

我的程序有一个组件——称为调度程序——它允许其他组件注册它们想要被回调的时间点。这应该像 Unix cron 服务一样工作,即您告诉调度程序“在每整整一小时后十分钟通知我”。

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

这是我的方法,是否有一个图书馆已经做了这些事情?也可以随意提出改进建议。

注册对调度程序的调用传递:

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

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

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


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

4

8 回答 8

8

查找石英

于 2009-03-05T10:33:58.567 回答
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);

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

实际上,您可以简单地重用 Quartz 的CronTrigger类来获得“下一个执行时间”。该类是完全独立的,不依赖于从Quartz“上下文”中调用。一旦您将下一个执行时间设置为Dateor ,您就可以像上面那样long使用 JavaScheduledExecutorService

于 2009-03-05T11:10:46.077 回答
4

Quartz是该领域的大而明显的强者,但也有一些替代品可供探索。

Cron4j是一个足够好的库,它比 Quartz 更轻量级。它提供了很好的文档,并会做你想做的事。

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

因为你想要更小的东西(刚刚注意到你的编辑),所以从 Quartz 和我上面提到的两个 HA-JDBC 类中获取 CronExpression。这样就可以了。

于 2009-03-05T10:58:31.583 回答
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();
    }
  }

}

如前所述如果您需要,java.util.concurrentExecutorService提供了更丰富的 API。

于 2009-03-05T12:31:25.010 回答
1

我强烈推荐cron4j(已经提到过)而不是 Quartz,除非你绝对需要 Quartz 的一些更高级和复杂的特性。Cron4j 很好地专注于它应该做的事情,拥有不错的文档,并且不是一个厨房水槽解决方案。

于 2010-04-07T06:48:54.463 回答
0

通常推荐使用Quartz 调度器。

于 2009-03-05T10:35:54.617 回答
0

不敢相信 java.util.Timer 被选为答案。石英确实是一个更好的选择。

与 java.util.Timer 相比,quartz 的一大优势在于,使用quartz,作业可以存储在数据库中。结果,一个 jvm 可以调度,另一个可以执行。此外(显然)该请求在 jvm 重新启动后仍然存在。

于 2009-03-07T07:54:39.743 回答
0

可能更有趣的是,如果您想使用更适合 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

我很高兴我没有陷入纠缠不清的依赖关系中。恭喜班级作者!

它一直像冠军一样工作。

于 2010-02-11T15:57:32.883 回答