让我烦恼的错误与this ticket相同.基本上,如果您将操作系统时钟更改为过去的某个日期,则在更改时 hibernate 的所有线程都不会醒来。
我正在开发的应用程序旨在 24/24 运行,我们希望能够在不停止它的情况下更改操作系统日期(例如,从夏令时切换到冬令时)。目前发生的情况是,当我们将日期更改为过去时,应用程序的某些部分就会卡住。我在 Windows XP 和 Linux 2.6.37 以及最近的 JVM (1.6.0.22) 的多台机器上观察到这一点。
我尝试了许多 Java hibernate 原语,但它们都有相同的行为:
- Thread.sleep(long)
- Thread.sleep(long, int)
- 对象.等待(长)
- Object.wait(long, int)
- Thread.join(long)
- Thread.join(long, int)
- LockSupport.parkNanos(长)
- java.util.Timer
- javax.swing.Timer
现在,我不知道要解决这个问题。我认为我无能为力防止 hibernate 线程卡住。但至少,我想在检测到危险的系统时钟更改时警告用户。
我想出了一个检测此类变化的监控线程:
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();
问题在于,这会使应用程序在空闲时从 2% 的 CPU 使用率增长到 15%。
您是否有解决原始问题的想法,或者您是否可以想出另一种方法来监控线程卡住的出现?
编辑
Ingo 建议不要碰系统时钟。我同意通常不需要它。问题是我们无法控制客户使用他们的计算机做什么(我们计划销售数百份)。
更糟的是:我们的一台机器在没有任何人工干预的情况下出现了这个问题。我猜操作系统 (Windows XP) 会定期将其时钟与 RTC 时钟同步,这会使操作系统时钟自然地回到过去。
结语
我发现我问题中的某些陈述是错误的。我最初的问题实际上有两个不同的原因。现在,我可以肯定地说两件事:
仅在我的机器上(带有内核 2.6.37 和 OpenJDK 64 位 1.6.0_22 的 archlinux),
Thread.sleep
,Object.wait
,Thread.join
,LockSupport.parkNanos
有同样的问题:它们只有在系统时钟到达“目标”唤醒时间时才会唤醒。然而,我的 shell 中的一个简单的sleep
并没有出现这个问题。在我测试的所有机器上(包括我的),
java.util.Timer
和java.swing.Timer
都有同样的问题(它们被阻塞了直到达到“目标”时间)。
所以,我所做的是用一个更简单的实现替换了所有 java 的 Timer
。这解决了除我之外的所有机器的问题(我只希望我的机器是异常(exception)而不是规则)。
最佳答案
根据错误票,您的线程没有被卡住,一旦时钟 catch 修改前的位置,它们就会恢复(因此,如果他们将时钟后移一个小时,您的线程将在 1 小时后恢复) .
当然,那仍然不是很有用。根本原因似乎是 Thread.sleep()
解析为系统调用,该系统调用使线程进入 hibernate 状态,直到将来某个特定的时间戳,而不是特定的持续时间。要解决这个问题,您需要实现您自己的 Thread.sleep()
版本,该版本使用 System.nanoTime()
而不是 System.currentTimeMillis()
或任何其他时间相关的 API。但是,我不能说如何在不使用内置 Thread.sleep()
的情况下做到这一点。
编辑:
或者,如果您使用另一种语言(如 C 或您喜欢的任何其他语言)创建一些外部应用程序,该应用程序除了等待指定的持续时间然后退出外什么都不做。然后,不用在 Java 中调用 Thread.sleep(),您可以生成此外部进程的一个新实例,然后对其调用 waitFor()。这将出于所有实际目的“hibernate ”Java 线程,只要您的外部应用程序能够 hibernate 正确的时间,它就会在正确的时间恢复,而不会被卡住,也不会扰乱 CPU。
似乎要解决这个问题还有很长的路要走,但这是我能想到的唯一可行的解决方法。此外,鉴于生成外部进程是一项相对昂贵的操作,如果您 hibernate 时间相对较长(例如数百毫秒或更长时间),它可能效果最佳。对于较短的持续时间,它可能会继续消耗 CPU。
关于更改操作系统时间时 sleep() 中的 Java 错误 : any workaround?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5909574/