17

正在使用Java Timer,然后切换到ScheduledExecutorService,但我的问题没有解决。因为在系统时间更改之前(通过 ntpd)计划的任务不会在指定的延迟时执行。没有日志,因为没有任何事情发生:(。

在 64 位 Linux 上的目标中使用 jre 1.6.0_26 64 位。

更新: ScheduledExecutorService 在 Windows 上运行良好。问题仅出现在运行 64 位 JVM 的基于 64 位 Linux 的系统上。它在运行 32 位 JVM 的 64 位 linux 上运行良好……奇怪。也没有在任何博客上找到任何相同的参考。

IBM 的 JAVA SDK 也有同样的问题(ibm-java-sdk-7.0-0.0-x86_64-archive.bin)。

我已经针对 JDK 7139684提交了缺陷,它被接受但已被关闭并标记为 6900441的副本。请投票给它,如果你觉得修复它值得……我不知道为什么它已经好几年没有修复了

这是我用来测试此问题的示例代码:

package test;

import java.io.IOException;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author yogesh
 *
 */
public class TimerCheck  implements Runnable {

    ScheduledExecutorService worker;


    public TimerCheck(ScheduledExecutorService worker) {
        super();
        this.worker = worker;
        this.worker.schedule(this, 1, TimeUnit.SECONDS);
    }

    private static void update() {
        System.out.println("TimerCheck.update() "+new Date(System.currentTimeMillis()));
    }

    @Override
    public void run() {
            update();
            worker.schedule(this, 1, TimeUnit.SECONDS);
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        ScheduledExecutorService worker = Executors.newScheduledThreadPool(1);
        new TimerCheck(worker);
    }

}
4

2 回答 2

7

JVM在系统时间向后更改期间的整体调度存在错误,这也影响了非常基本的 Object.wait 和 Thread.sleep 方法。当系统时间切换回几秒钟时,让 Java App 继续运行变得太冒险了。你永远不知道你的 Java 应用程序最终会是什么。

所以我们决定:

  • 编写看门狗脚本(非 Java :))来检查时间变化。
  • 如果时间切换回一定量,关闭并重新启动 Java 应用程序。

另一种可能性是迁移到 32 位 JVM,但我们使用的是 JNI,然后目标平台上使用的本机库不兼容 32 位。同样根据我们的经验,32 位 JVM 将我们的目标限制为 1.6G 堆,这对我们来说根本不够用。

我知道我们的解决方案不是最优雅的解决方案,但在 JVM 修复或找到更好的解决方案之前,似乎没有其他方法。

编辑: 除了上述解决方案外,我们还在考虑 Chris 的第一个建议:

  • 将 NTP 配置为永远不会有大的时间跳跃。只是慢慢地把时间流逝了。仅在停机期间手动应用大时间跳转。
于 2012-02-01T05:46:02.853 回答
2

我解决了同样的问题,但没有找到解决方案。我查看了 Quartz 和所有内置的 JRE 方法。nanotime 方法可能使用每个 CPU 的单调时钟,但我相信,如果将线程迁移到另一个 CPU,您将面临巨大的跳跃风险。我的结论如下:

  1. 将 NTP 配置为永远不会有大的时间跳跃。只是慢慢地把时间流逝了。仅在停机期间手动应用大时间跳转。
  2. 使用多个较短的超时,并需要两个超时来声明远程机器死亡。如果您自己只有一个系统计时,这将无济于事,但可以帮助多个系统
  3. 故意使用远程机器向您发送定期唤醒。如果你自己的时钟在两次唤醒之间倒退,那么你知道你所有的计时器都会迟到。

基本上,这是一个巨大的痛苦,但没有灵丹妙药。

于 2012-01-28T13:46:38.843 回答