我写了一个简单的概念验证,用于调度一个runnable在给定的时间执行.代码完全按照预期工作,并在计划的时间执行runnable:

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

但是,如果我在程序运行时将系统置于睡眠状态,然后继续唤醒它,那么runnable将在计划时间之外执行:

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

假设操作系统在计划时间之前唤醒,我如何补偿这一点,以便Runnable仍然在预期的时间执行?

推荐答案

正如我在 comments 中指出的,您观察到的系统挂起/Hibernate 延迟通过ScheduledExecutorService计划的任务执行的问题似乎是已知的问题JDK-8146527,最初提交于2016年初.ScheduledExecutorService或其标准实现ScheduledThreadPoolExecutor似乎没有任何可用的解决方案.

但是,您可以通过不同的调度机制(如java.util.Timer)来解决此问题.此外,该特定类为scheduling execution at a particular point in time提供了内置方法,因此如果您还没有延迟,您应该不需要自己计算延迟.如果您 Select 使用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()));
}

该方法用于在指定时间调度任务,但我没有很好的方法在系统挂起的特定情况下对其进行测试.如果它不能解决这个问题,那么您可能需要找到一个可以处理它的第三方调度程序,或者编写您自己的调度程序.

Java相关问答推荐

Java 21虚拟线程会解决转向react 式单线程框架的主要原因吗?

Java Streams在矩阵遍历中的性能影响

Java函数式编程中的双值单值映射

如何以干净的方式访问深度嵌套的对象S属性?

Chunk(Int)已弃用并标记为要删除

按属性值从流中筛选出重复项

Mac上的全屏截图在使用JavaFX时不能正常工作吗?

使用UTC时区将startDatetime转换为本地时间

Arrays.hashcode(int[])为不同的元素提供相同的散列

如何将Pane的图像快照保存为BMP?

内存和硬盘中的Zip不同,这会导致下载后的Zip损坏

在VS代码中,如何启用Java Main函数的&Q;Run|DEBUG&Q;代码?

try 使用Spring集成和MySQL实现发件箱模式时,锁定等待超时

如何在太阳系模拟器中添加月球?

JavaFX:无论何时显示应用程序,如何更改组件/ node 位置?

具有最大共同前景像素的图像平移优化算法

rest api服务 spring 启动中出现IllegalFormatConversionException

如何用Micrometer&;斯普肯

在输入端没有可行的替代方案'; Select *';

对于 Hangman 游戏,索引 0 超出长度 0 的范围