我编写了一个简单的概念验证,用于安排可运行程序在给定时间执行。代码完全按照预期工作,并在预定时间执行可运行程序:
public static void main(String[] args) throws InterruptedException, ExecutionException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss a");
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Runnable task = new Runnable() {
public void run() {
System.out.println("Task Ran @ " + formatter.format(LocalDateTime.now()));
}
};
LocalDateTime now = LocalDateTime.now();
LocalDateTime runTime = ciel(ChronoUnit.MINUTES, now.plusMinutes(1));
Duration duration = Duration.between(now, runTime);
long initialDelay = duration.toMillis();
System.out.println("Schedule @ " + formatter.format(now));
System.out.println("Expected @ " + formatter.format(runTime));
scheduler.schedule(task, initialDelay, TimeUnit.MILLISECONDS);
scheduler.shutdown();
scheduler.awaitTermination(2, TimeUnit.MINUTES);
}
private static LocalDateTime ciel(TemporalUnit precision, LocalDateTime time) {
return time.truncatedTo(precision).plus(1, precision);
}
输出:
Schedule @ 12/18/2023 11:31:47 AM
Expected @ 12/18/2023 11:33:00 AM
Task Ran @ 12/18/2023 11:33:00 AM
但是,如果我在程序运行时将系统置于睡眠状态并稍后唤醒它,则可运行程序将在计划时间之外执行:
Schedule @ 12/18/2023 11:35:53 AM
Expected @ 12/18/2023 11:37:00 AM
Task Ran @ 12/18/2023 11:39:13 AM
假设操作系统在预定时间之前唤醒,我该如何补偿这一点,以便可运行程序仍然按预期执行?
正如我在评论中指出的,您观察到的系统挂起/睡眠延迟执行通过
ScheduledExecutorService
安排的任务的问题似乎是已知问题 JDK-8146527,最初于 2016 年初提交。似乎是 ScheduledExecutorService
或其标准实现 ScheduledThreadPoolExecutor
中可用的任何解决方法。
java.util.Timer
。此外,该特定类提供了一种内置方法,用于“在特定时间点调度执行”,因此,如果您还没有延迟,则无需自己计算延迟。如果您选择使用 Timer
那么您应该确保阅读并理解其文档中表达的警告。看起来可能是这样的:import java.util.Timer; // Do not confuse this with javax.swing.Timer
// ...
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss a");
Timer timer = new Timer();
TimerTask task = new TimerTask() {
public void run() {
System.out.println("Task Ran @ " + formatter.format(ZonedDateTime.now()));
timer.cancel();
}
};
ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime runTime = now.plusMinutes(1);
System.out.println("Schedule @ " + formatter.format(now));
System.out.println("Expected @ " + formatter.format(runTime));
timer.schedule(task, Date.from(runTime.toInstant()));
}
该方法适用于在指定时间安排任务,但我没有好的方法在系统挂起的特定情况下测试它。如果它不能解决该问题,那么您可能需要找到可以处理它的第三方调度程序,或者编写您自己的调度程序。