更改操作系统时间时睡眠()中的Java错误:任何解决方法?

bar*_*jak 16 java sleep clock

惹恼我的错误与此票相同.基本上,如果您将OS时钟更改为过去的日期,则更改时正在休眠的所有线程都不会被唤醒.

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

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

  • 了Thread.sleep(长)
  • Thread.sleep(long,int)
  • 的Object.wait(长)
  • Object.wait(long,int)
  • 的Thread.join(长)
  • Thread.join(long,int)
  • LockSupport.parkNanos(长)
  • 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();
Run Code Online (Sandbox Code Playgroud)

问题是这使得应用程序在空闲时从2%的CPU使用率增长到15%.

你有想法解决原始问题,或者你能想到另一种监控线程冻结外观的方法吗?

编辑

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

更糟糕的是:我们的一台机器没有任何人工干预就会出现这个问题.我想OS(Windows XP)会定期将其时钟与RTC时钟同步,这使得OS时钟自然会及时回溯.

结语

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

  1. 在我的机器只(ArchLinux的内核2.6.37与OpenJDK的64位1.6.0_22) ,Thread.sleep,,Object.wait 有同样的问题:他们醒来,只有当系统时钟到达觉醒的"目标"的时间.但是,我的shell中的一个简单不会出现问题.Thread.joinLockSupport.parkNanossleep

  2. 在我测试的所有机器上(包括我的),java.util.Timer并且java.swing.Timer具有相同的问题(它们被阻塞直到达到"目标"时间).

所以,我所做的是Timer通过更简单的实现替换了所有的java .这解决了除了我的所有机器的问题(我只希望我的机器不仅仅是一个规则).

aro*_*oth 9

根据错误故障单,你的线程没有被冻结,一旦时钟赶上它修改之前的位置就会恢复(所以如果他们将它移回一小时,你的线程将在1小时内恢复).

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

编辑:

或者,如果您使用另一种语言(如C或您喜欢的任何其他语言)创建一些外部应用程序,除了等待指定的持续时间然后退出,该怎么办.然后,不是在Java中调用Thread.sleep(),而是可以生成此外部进程的新实例,然后在其上调用waitFor().这将"睡眠"Java线程用于所有实际目的,并且只要您的外部应用程序能够在正确的持续时间内休眠,它将在正确的时间恢复而不会被冻结并且不会使CPU崩溃.

似乎还有很长的路可以解决问题,但这是我能想到的唯一可行的解​​决方法.此外,鉴于产生外部过程是一个相对昂贵的操作,如果你睡了相当长的时间(如几百毫秒或更长),它可能是最好的.对于较短的持续时间,它可能只是继续颠簸CPU.