更改操作系统时间时睡眠()中的Java错误:任何解决方法? [英] Java bug in sleep() when changing OS time : any workaround?

查看:152
本文介绍了更改操作系统时间时睡眠()中的Java错误:任何解决方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



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




  • Thread.sleep(long)

  • Thread.sleep( long,int)

  • Object.wait(long)

  • Object.wait(long,int)

  • Thread.join(长)

  • Thread.join(long,int)

  • LockSupport.parkNanos(long)

  • java.util.Timer

  • javax.swing.Timer



现在,我不想解决这个问题。我认为我无法阻止睡眠线冻结。但至少,我希望在检测到危险的系统时钟更改时警告用户。



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

 线程t =新线程(新的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(时钟监视器);
t.setPriority(Thread.MIN_PRIORITY);
t.setDaemon(true);
t.start();

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

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



编辑



Ingo建议不要触摸系统时钟。我同意通常不需要它。问题是我们无法控制客户使用他们的计算机做什么(我们计划销售数百份)。



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



Epilogue



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


  1. 仅在我的机器上(带有内核2.6.37的带有OpenJDK 64的archlinux)位1.6.0_22), Thread.sleep Object.wait Thread.join LockSupport.parkNanos 有同样的问题:它们仅在系统时钟达到唤醒的目标时间时唤醒。但是,我的shell中的一个简单的 sleep 没有出现问题。


  2. 在所有机器上我测试(包括我的), java.util.Timer java.swing.Timer 有同样的问题(它们是阻止,直到达到目标时间。)


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

解决方案

根据错误故障单,您的线程不会被冻结,它们将在时钟赶上修改之前恢复(因此,如果它们将它移回一小时,您的线程将在1小时内恢复)。



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



编辑:



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



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


The bug that annoys me is the same than this ticket. Basically, if you change the OS clock to a date in the past, all the thread that were sleeping at the time of the change won't wake up.

The application I am developping is meant to be running 24/24, and we would like to be able to change the OS date without stopping it (for example, to switch from summer time to winter time). What happens for the moment is that when we change the date to the past, then some parts of the application just freeze. I observed that on multiple machine, on Windows XP and Linux 2.6.37, and with a recent JVM (1.6.0.22).

I tried many Java sleeping primitives, but they all have the same behavior :

  • Thread.sleep(long)
  • Thread.sleep(long, int)
  • Object.wait(long)
  • Object.wait(long, int)
  • Thread.join(long)
  • Thread.join(long, int)
  • LockSupport.parkNanos(long)
  • java.util.Timer
  • javax.swing.Timer

Now, I am out of idea to work around this problem. I think there is nothing I can do to prevent the sleeping threads to freeze. But I would like, at least, to warn the user when a dangerous system clock change is detected.

I came up with a monitoring thread that detects such changes :

    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();

The problem is that this makes the application grow from 2% CPU usage to 15% when idle.

Do you have an idea to work around the original problem, or can you think of another way to monitor the appearance of thread freeze ?

Edit

Ingo suggested not to touch the system clock. I agree that it's generally not needed. The problem is that we don't control what our clients do with their computers (we plan to sell hundred of copies).

Worse : one of our machine exhibits this problem without any manual intervention. I guess the OS (Windows XP) regularly synchronizes its clock to the RTC clock, and this makes the OS clock go back in time naturally.

Epilogue

I found out that some statements in my question were wrong. There are actually two separate causes involved in my initial problem. Now, I can say two things for sure :

  1. On my machine only (archlinux with kernel 2.6.37 with an OpenJDK 64 bits 1.6.0_22), Thread.sleep, Object.wait, Thread.join, LockSupport.parkNanos have the same problem : they wake up only when the system clock reaches the "target" time of awakening. However, a simple sleep in my shell does not exhibit the problem.

  2. On all the machines I tested (included mine), java.util.Timer and java.swing.Timer have the same problem (they are blocked until the "target" time is reached).

So, what I've done is that I replaced all the java's Timer by a simpler implementation. This solves the problem for all the machines but mine (I just hope my machine is an exception more than a rule).

解决方案

According to the bug ticket, your threads aren't frozen, they will resume once the clock catches up to where it was before it was modified (so if they moved it back an hour, your threads will resume in 1 hour).

Of course, that is still not very useful. The root cause seems to be that Thread.sleep() resolves to a system call that puts the thread to sleep until some specific timestamp in the future, rather than for a specified duration. To work around it you would need to implement your own version of Thread.sleep() that uses System.nanoTime() instead of System.currentTimeMillis() or any other time-dependent API. How to do that without using the built-in Thread.sleep() I can't say, however.

Edit:

Or, what if you create some external app in another language (like C or whatever else you prefer) that does nothing but wait for a specified duration and then exit. Then instead of calling Thread.sleep() in Java, you can spawn a new instance of this external process, and then call waitFor() on it. This will "sleep" the Java thread for all practical purposes, and so long as your external app is able to sleep for the correct duration, it will resume at the correct time without getting frozen and without thrashing the CPU.

Seems like a long way to go to fix the issue, but it's the only feasible workaround that I can think of. Also, given that spawning an external process is a relatively expensive operation, it probably works best if you are sleeping for a relatively long time (like several hundred ms or more). For shorter durations it might just continue thrashing the CPU.

这篇关于更改操作系统时间时睡眠()中的Java错误:任何解决方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆