Java library class to handle scheduled execution of “callbacks”?

痞子三分冷 提交于 2019-12-03 13:45:23
McDowell

If your needs are simple, consider using 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();
    }
  }

}

As has been noted, the ExecutorService of java.util.concurrent offers a richer API if you need it.

Lookup Quartz

If your objects know exactly the individual points in time which they wish to be executed, then you could use a java.util.concurrent.ScheduledExecutorService. Then they simple call:

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

You'd only need to use Quartz if you want the scheduler itself to handle functionality like "execute every 5 seconds", or if you want complex behaviour around missed executions, or the persistence of the execution audit trail.

You can actually trivially re-use Quartz's CronTrigger class to get a "next execution time". The class is completely standalone and does not depend on being invoked from within the Quartz "context". Once you have the next execution time as a Date or long, you can just use the Java ScheduledExecutorService as above

Quartz is the big and obvious powerhouse in this area, but there are some alternatives to explore.

Cron4j is a decent enough library, that is a little more lightweight than Quartz. It provides good documentation and will do what you want.

Probably more interesting is if you want to use a library that fits better with Java's concurrency libraries (particularly Executors and ScheduledExecutors) then HA-JDBC has a CronExecutorService interface, implemented by its CronThreadPoolExecutor. Now, interestingly, it has a dependency on Quartz (to provide the CronExpression class), but I find that the two together work better than just Quartz alone. If you don't want large dependencies, its easy to extract the handful of classes from Quartz and HA-JDBC that make this happen.

Since you want something much smaller (just noticed your edit), grab CronExpression from Quartz, and the two HA-JDBC classes I mentioned above. That'll do it.

I would strongly recommend cron4j (already mentioned) over Quartz, unless you absolutely need some of more advanced and complex features of Quartz. Cron4j focuses nicely on what it is supposed to do, has decent documentation, and is not a kitchen-sink solution.

Marko

Quartz scheduler is usually recommended.

Can't believe java.util.Timer was voted as the answer. Quartz is really a much better choice.

A big advantage of quartz over java.util.Timer is that with quartz the jobs can be stored in the db. As a result one jvm can schedule and another can execute. Also (obviously) the request survives across jvm restarts.

Probably more interesting is if you want to use a library that fits better with Java's concurrency libraries (particularly Executors and ScheduledExecutors) then HA-JDBC has a CronExecutorService interface, implemented by its CronThreadPoolExecutor. Now, interestingly, it has a dependency on Quartz (to provide the CronExpression class), but I find that the two together work better than just Quartz alone. If you don't want large dependencies, its easy to extract the handful of classes from Quartz and HA-JDBC that make this happen.

I just wanted to say that I tried extracting these classes, and it worked! I needed these three classes:

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

And I only had to do these minor tweaks:

  • Removing the logger from CronThreadPoolExecutor (it was created but never used)
  • Moved the constant YEAR_TO_GIVEUP_SCHEDULING_AT from CronTrigger to CronExpression

I was thrilled that I didn't get stuck pull in a tangle of dependencies. Congrats to the class authors!

And it's been working like a champ.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!