date/time 处的调用方法

Call method at date/time

我正在寻找一种 现代 方式来在给定的 date/time(特别是 ZonedDateTime)处执行给定的方法。

我知道 Timer class 和 Quartz 库,如下所示(线程包括完整的解决方案):

但这些线程相当陈旧,从那时起就没有使用新的 Java 功能和库元素。特别是,获得任何类型的 Future 对象都会非常方便,因为它们提供了一种简单的机制来取消它们。

所以请不要建议涉及 TimerQuartz 的解决方案。另外,我想要一个 vanilla 解决方案,不使用任何外部库。但是为了问答,也请随意提出建议。

ScheduledExecutorService

您可以使用 ScheduledExecutorService (documentation) class, which is available since Java 5. It will yield a ScheduledFuture (documentation) 来监视执行并取消它。

特别是方法:

ScheduledFuture<?> schedule​(Runnable command, long delay, TimeUnit unit)

哪个

Submits a one-shot task that becomes enabled after the given delay.

但您也可以研究其他方法,具体取决于实际用例(scheduleAtFixedRate 和接受 Callable 而不是 Runnable 的版本)。

自从 Java 8(Streams、Lambdas、...)以来,这个 class 变得更加方便,因为在旧 TimeUnit 和新版本之间提供了简单的转换方法较新的 ChronoUnit(对于您的 ZonedDateTime),以及提供 Runnable command 作为 lambda 或方法参考的能力(因为它是 FunctionalInterface)。


例子

让我们看一个执行您要求的示例:

// Somewhere before the method, as field for example
// Use other pool sizes if desired
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

public static ScheduledFuture<?> scheduleFor(Runnable runnable, ZonedDateTime when) {
    Instant now = Instant.now();
    // Use a different resolution if desired
    long secondsUntil = ChronoUnit.SECONDS.between(now, when.toInstant());

    return scheduler.schedule(runnable, secondsUntil, TimeUnit.of(ChronoUnit.SECONDS));
}

一个调用很简单:

ZonedDateTime when = ...
ScheduledFuture<?> job = scheduleFor(YourClass::yourMethod, when);

然后您可以使用 job 来监视执行并在需要时取消它。示例:

if (!job.isCancelled()) {
    job.cancel(false);
}

备注

你可以将方法中的ZonedDateTime参数换成Temporal,然后它也接受其他date/time格式。

完成后不要忘记关闭 ScheduledExecutorService。否则你将有一个线程 运行,即使你的主程序已经完成。

scheduler.shutdown();

请注意,我们使用 Instant 而不是 ZonedDateTime,因为时区信息与我们无关,只要时差计算正确即可。 Instant 始终代表 UTC 时间,不会出现 DST 奇怪的 现象。 (虽然这对这个应用程序来说并不重要,但它更干净)。