更改操作系统时间时睡眠中的Java bug():有解决方法吗?

更改操作系统时间时睡眠中的Java bug():有解决方法吗?,java,sleep,clock,Java,Sleep,Clock,惹恼我的虫子和我的一样。基本上,如果您将操作系统时钟更改为过去的日期,那么在更改时处于休眠状态的所有线程都不会唤醒 我正在开发的应用程序将24/24运行,我们希望能够在不停止的情况下更改操作系统日期(例如,从夏季切换到冬季)。目前的情况是,当我们将日期更改为过去时,应用程序的某些部分就会冻结。我观察到,在多台机器上,在WindowsXP和Linux2.6.37上,以及在最近的JVM(1.6.0.22)上都存在这种情况 我尝试了许多Java睡眠原语,但它们都有相同的行为: 线程。睡眠(长) 线程

惹恼我的虫子和我的一样。基本上,如果您将操作系统时钟更改为过去的日期,那么在更改时处于休眠状态的所有线程都不会唤醒

我正在开发的应用程序将24/24运行,我们希望能够在不停止的情况下更改操作系统日期(例如,从夏季切换到冬季)。目前的情况是,当我们将日期更改为过去时,应用程序的某些部分就会冻结。我观察到,在多台机器上,在WindowsXP和Linux2.6.37上,以及在最近的JVM(1.6.0.22)上都存在这种情况

我尝试了许多Java睡眠原语,但它们都有相同的行为:

  • 线程。睡眠(长)
  • 线程睡眠(长,int)
  • 对象。等待(长)
  • Object.wait(长,int)
  • 线程连接(长)
  • Thread.join(长,int)
  • 锁支架。Parknos(长)
  • java.util.Timer
  • javax.swing.Timer
现在,我不想解决这个问题。我想我没办法阻止休眠线程冻结。但我想至少在检测到危险的系统时钟变化时向用户发出警告

我想出了一个监视线程来检测这些变化:

    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            long ms1 = System.currentTimeMillis();
            long ms2;
            while(true) {
                ms2 = ms1;
                ms1 = System.currentTimeMillis();
                if (ms1 < ms2) {
                    warnUserOfPotentialFreeze();
                }
                Thread.yield();
            }                    
        }
    });
    t.setName("clock monitor");
    t.setPriority(Thread.MIN_PRIORITY);
    t.setDaemon(true);
    t.start();
Thread t=新线程(new Runnable(){
@凌驾
公开募捐{
long ms1=System.currentTimeMillis();
长ms2;
while(true){
ms2=ms1;
ms1=System.currentTimeMillis();
如果(ms1
问题是,这使得应用程序从2%的CPU使用率增长到空闲时的15%

您是否有解决原始问题的想法,或者您是否可以想出另一种方法来监视线程冻结的外观

编辑 Ingo建议不要触摸系统时钟。我同意这通常是不需要的。问题是我们无法控制客户对电脑的操作(我们计划销售数百份)

更糟糕的是:我们的一台机器在没有任何手动干预的情况下出现了此问题。我猜操作系统(Windows XP)会定期将时钟与RTC时钟同步,这会使操作系统时钟自然地回到时间上

后记 我发现我问题中的一些陈述是错误的。我最初的问题实际上有两个不同的原因。现在,我可以肯定地说两件事:

  • 仅在我的机器上(内核为2.6.37、OpenJDK 64位1.6.022的archlinux),
    线程、睡眠、对象、等待、
    线程、加入、
    锁支持。Parknos也有同样的问题:它们只有在系统时钟到达“目标”唤醒时间时才会唤醒。然而,在我的shell中,一个简单的
    睡眠
    不会出现问题

  • 在我测试的所有机器(包括我的机器)上,
    java.util.Timer
    java.swing.Timer
    都有相同的问题(它们会被阻止,直到达到“目标”时间)


  • 因此,我所做的是用一个更简单的实现替换了所有java的
    计时器。这解决了除我之外所有机器的问题(我只希望我的机器是一个例外,而不是一个规则)。

    @Op。您实现了一些看起来像“忙等待”的功能,这将始终消耗大量资源


    我同意其他人的看法,我不明白为什么从夏季到冬季,你需要更改系统时钟。

    根据错误记录,你的线程没有冻结,一旦时钟恢复到修改前的位置,它们就会恢复(因此,如果他们将其向后移动一小时,你的线程将在1小时后恢复)

    当然,这仍然不是很有用。根本原因似乎是
    Thread.sleep()
    解析为一个系统调用,该调用将线程置于睡眠状态,直到将来某个特定的时间戳,而不是指定的持续时间。要解决这个问题,您需要实现自己版本的
    Thread.sleep()
    ,它使用
    System.nanoTime()
    而不是
    System.currentTimeMillis()
    或任何其他依赖于时间的API。但是,我不知道如何在不使用内置的
    Thread.sleep()
    的情况下做到这一点

    编辑:

    或者,如果您用另一种语言(如C或其他您喜欢的语言)创建了一个外部应用程序,它只需等待指定的时间,然后退出,该怎么办。然后,您可以生成此外部进程的新实例,然后对其调用waitFor(),而不是在Java中调用Thread.sleep()。这将“休眠”Java线程,用于所有实际用途,只要您的外部应用程序能够在正确的时间内休眠,它将在正确的时间恢复,而不会冻结,也不会冲击CPU


    解决这个问题似乎还有很长的路要走,但这是我能想到的唯一可行的解决办法。此外,鉴于生成外部进程是一项相对昂贵的操作,如果您睡眠时间相对较长(比如几百毫秒或更长),那么它可能最有效。对于较短的持续时间,它可能会继续冲击CPU。

    正如其他人所说,您绝对不应该更改系统时钟。时间戳(从纪元算起的毫秒)在世界上所有计算机上都是一致的,但本地时间取决于您的位置、对夏令时的观测等等。因此,问题在于操作系统的区域设置和时间/日期设置

    (尽管如此,我还是同意,如果系统时钟确实发生了变化,JVM应该检测到这一点,并更新或唤醒休眠线程以应对这种情况