【问题标题】:Java scheduler which is completely independent of system time changes完全独立于系统时间变化的 Java 调度程序
【发布时间】:2012-01-28 10:05:35
【问题描述】:

使用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);
    }

}

【问题讨论】:

  • 此示例代码在我的机器 ubuntu 11.10 [32 位]、JVM 1.6.0_21 [32 位] 上运行良好。更改系统时钟不会影响执行。
  • 是的,还检查了它在运行 32 位 jvm 的 64 位 linux 上运行良好 :(。相应地更新了我的问题。
  • 我只想指出,在这种情况下指定纳秒是没有意义的。在内部,代码无论如何都会更改为毫秒。所以worker.schedule(this, 1000000000, TimeUnit.NANOSECONDS); 等同于worker.schedule(this, 1, TimeUnit.SECONDS);
  • @ChrisDolan 谢谢。但我更关注调度问题。
  • 你能把系统设置为GMT吗? GMT 没有夏令时。

标签: java timer 32bit-64bit scheduler ntp


【解决方案1】:

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

所以我们决定:

  • 编写 watch dog 脚本(非 Java :))来检查时间变化。
  • 如果时间倒退一定时间,请关闭并重新启动 Java 应用程序。

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

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

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

  • 将 NTP 配置为永远不会有大的时间跳跃。只消磨时间 慢慢地。仅在停机期间手动应用大时间跳转。

【讨论】:

  • 接受此答案作为解决方法:(。仍在等待适当的解决方案。
  • 我不明白,它对JVM的影响有多深。从您的回答看来,这似乎是一个严重的错误。例如,如果我使用 Object.wait(1),并且时钟向后移动——这是否意味着线程永远不会被唤醒?
  • @BegemoT 这并不意味着它永远不会醒来......但它会在(时钟向后移动+ 1)之后醒来。这是危险的。而且它只发生在 64 位 linux 上的 64 位 JVM 上。
【解决方案2】:

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

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

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

【讨论】:

  • 但是您最终是否应用了这些解决方案。对我来说,这些是不可能的:)。
  • @YoK,三个都是。我的场景是检测主服务是否/何时死亡,以便备份可以自动接管。在我使用的所有 NTP 配置中,行为是:如果同步在 N 分钟内(例如 2 分钟),则以每分钟 X 毫秒的速率(例如 20 毫秒)缓慢地将时钟转向正确的时间。如果同步时间超过 N 分钟,则 NTP 会发出系统日志消息警告错误并进入被动状态。
  • 有趣...会请我的 NTP 人员看一下。
  • nanoTime 应该在 CPU 之间保持一致 - 请参阅 stackoverflow.com/a/4588605/116639
  • @TomAnderson 很酷,感谢您的更新。我的信息已经过时了。有没有利用这一点的定时器实现?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-01
  • 2010-10-30
  • 2011-02-12
  • 2013-11-13
相关资源
最近更新 更多