更改操作系统时间时睡眠中的Java bug():有解决方法吗?
惹恼我的虫子和我的一样。基本上,如果您将操作系统时钟更改为过去的日期,那么在更改时处于休眠状态的所有线程都不会唤醒 我正在开发的应用程序将24/24运行,我们希望能够在不停止的情况下更改操作系统日期(例如,从夏季切换到冬季)。目前的情况是,当我们将日期更改为过去时,应用程序的某些部分就会冻结。我观察到,在多台机器上,在WindowsXP和Linux2.6.37上,以及在最近的JVM(1.6.0.22)上都存在这种情况 我尝试了许多Java睡眠原语,但它们都有相同的行为:更改操作系统时间时睡眠中的Java bug():有解决方法吗?,java,sleep,clock,Java,Sleep,Clock,惹恼我的虫子和我的一样。基本上,如果您将操作系统时钟更改为过去的日期,那么在更改时处于休眠状态的所有线程都不会唤醒 我正在开发的应用程序将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时钟同步,这会使操作系统时钟自然地回到时间上
后记
我发现我问题中的一些陈述是错误的。我最初的问题实际上有两个不同的原因。现在,我可以肯定地说两件事:
线程、睡眠、对象、等待、线程、加入、锁支持。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应该检测到这一点,并更新或唤醒休眠线程以应对这种情况