Windows 上的 Java 准确睡眠

Posted

技术标签:

【中文标题】Windows 上的 Java 准确睡眠【英文标题】:Accurate Sleep for Java on Windows 【发布时间】:2010-10-23 20:56:09 【问题描述】:

有谁知道一个库,它为 Java 提供了一个错误不高于 1-2 毫秒的 Thread.sleep()?

我尝试了睡眠、错误测量和 BusyWait 的混合,但在不同的 Windows 机器上我没有得到可靠的结果。

如果实现也适用于 Linux 和 MacOS,它可以是本机实现。

编辑 Nick 提供的链接 (http://blogs.oracle.com/dholmes/entry/inside_the_hotspot_vm_clocks) 是一个非常好的资源,可以帮助您了解 java 的各种计时器/睡眠/时钟问题。

【问题讨论】:

我不坚持使用 Thread.sleep() ...我只想停止执行我的程序一段时间 (1-20)。如果有另一种方法可以频繁且准确地暂停和唤醒我的代码,请不要犹豫,指出另一种解决方案。 睡眠对于动画来说应该足够准确,只要它是 Java 1.5 或更高版本。 【参考方案1】:

这晚了约 5 个月,但可能对阅读此问题的人有用。我发现java.util.concurrent.locks.LockSupport.parkNanos()Thread.sleep() 的功能相同,但具有纳秒级精度(理论上),并且在实践中比Thread.sleep() 精度更高。这当然取决于您使用的 Java 运行时,所以是 YMMV。

看看:LockSupport.parkNanos

(我在 Sun 的 Linux 1.6.0_16-b01 VM 上验证了这一点)

【讨论】:

我刚才试了一下,不到1ms就停不下来,和Thread.sleep一样。 :( 取决于您的操作系统。在具有 > 2.6 内核的 Linux 上,它会。 天才!终于有办法让我保持警惕了。我从来都不是这个的任何java替代品的粉丝。这太干净了..【参考方案2】:

要提高睡眠粒度,您可以在此 Thread.sleep 页面尝试以下操作。

Windows 下 Thread.sleep() 的错误

如果时机对您来说至关重要 应用程序,然后是一个不优雅但 解决这些错误的实用方法 是让一个守护线程运行 在您的整个期间 简单地休眠的应用程序 大质数毫秒 (Long.MAX_VALUE 可以)。这边走, 中断周期将设置一次 每次调用您的应用程序, 最小化对系统的影响 时钟,并设置睡眠 粒度为 1ms,即使在 默认中断周期不是 15ms。

该页面还提到它会导致系统范围内的 Windows 发生变化,这可能会导致用户的时钟由于这个bug 而运行得很快。

编辑

提供有关此的更多信息 here 和关联的 bug report 来自 Sun。

【讨论】:

这很有帮助。我在 2 台不同负载的不同 Windows 机器上运行测试,平均误差为 0.8 毫秒。在 Linux 上,我的平均错误为 0.3 毫秒 - 谢谢你拯救了我的一天 :-) 太棒了,可惜它是一个 hack - 我目前使用类似的方法,但还没有为 Swing 动画找到同样功能和优雅的解决方案。如果有人更开明......【参考方案3】:

JDK 提供了 Timer 类。

http://java.sun.com/j2se/1.5.0/docs/api/java/util/Timer.html

阅读文档清楚地表明,除了使该框架成为通用框架的管道之外,它使用的没有什么比调用 Object.wait(timeout) 更复杂的了:

http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html#wait(long)

所以,你可以直接使用 Object#wait 自己。

除了这些考虑之外,事实仍然是 JVM 不能保证跨平台的时间准确性。 (阅读http://java.sun.com/j2se/1.5.0/docs/api/java/lang/System.html#currentTimeMillis() 上的文档)

如果您想在您的平台上获得尽可能高的计时精度,我认为您需要尝试一种将计时器和繁忙轮询相结合的折衷解决方案。有效地 Object#wait(1) -> System#nanoTime -> 计算 delta -> [必要时循环]。

如果您愿意自行开发,JNI 几乎可以为特定平台的解决方案敞开大门。我很高兴不知道 Window 的内部结构,但显然,如果主机操作系统确实提供了足够准确的实时计时器服务,那么设置 timerRequest(timedelta, callback) 本机库的准系统结构不应该是遥不可及的。

【讨论】:

【参考方案4】:

不幸的是,从 Java 6 开始,Windows 操作系统上所有与睡眠相关的方法 [包括 LockSupport.awaitNanos()] 都基于毫秒,正如上面几个人所提到的。

计算精确间隔的一种方法是“旋转产量”。方法 System.nanoTime() 为您提供相当精确的相对时间计数器。此调用的成本取决于您的硬件,大约为 2000-50 纳秒。

这里建议替代 Thread.sleep():

   public static void sleepNanos (long nanoDuration) throws InterruptedException 
        final long end = System.nanoTime() + nanoDuration;
        long timeLeft = nanoDuration;
        do 
            if (timeLeft > SLEEP_PRECISION)
                Thread.sleep (1);
            else
                if (timeLeft > SPIN_YIELD_PRECISION)
                    Thread.yield();

            timeLeft = end - System.nanoTime();
         while (timeLeft > 0);
    

这种方法有一个缺点 - 在等待命中 CPU 内核的最后 2-3 毫秒期间。请注意, sleep()/yield() 将与其他线程/进程共享。如果你愿意牺牲一点 CPU,这会给你very accurate sleep。

【讨论】:

好帖子。然而,牺牲 CPU 并不是一个很好的解决方案(确实,取决于您的用例) 如何计算 SPIN_YIELD_PRECISION?只有 1 毫秒吗? SLEEP_PRECISION = TimeUnit.MILLISECONDS.toNanos(2); SPIN_YIELD_PRECISION = TimeUnit.MILLISECONDS.toNanos(2); 给出了很好的结果,但是我没有在生产服务器上运行这些【参考方案5】:

您可以尝试使用新的并发库。比如:

private static final BlockingQueue SLEEPER = new ArrayBlockingQueue(1);
public static void main(String... args) throws InterruptedException 
    for(int i=0;i<100;i++) 
        long start = System.nanoTime();
        SLEEPER.poll(2, TimeUnit.MILLISECONDS);
        long time = System.nanoTime() - start;
        System.out.printf("Sleep %5.1f%n", time/1e6);
    

这会休眠 2.6 到 2.8 毫秒。

【讨论】:

您的意思是,“它的睡眠时间在 2.6 到 2.8 毫秒之间”? 谢谢你,若昂。我的大部分时间都是在微秒内完成的,有时我会把它们弄糊涂。 :-/ 我看到这种方法的结果有点随机,而 Any Malakov 的技巧似乎更精确。但我同意可能必须仔细选择旋转产量【参考方案6】:

Long.MAX_VALUE 破解是可行的解决方案。

我试过用Object.wait(int milis)代替Thread.sleep,但发现Object.wait和Thread.sleep一样准确(Windows下10ms)。如果没有 hack,这两种方法都不适合任何动画

【讨论】:

【参考方案7】:

听起来你需要一个 real-time Java 的实现。

【讨论】:

【参考方案8】:

在普通代码中使用Thread.sleep() 没有充分的理由——它(几乎)总是表明设计不好。最重要的是,没有保证线程会在指定时间后继续执行,因为Thread.sleep() 的语义只是停止执行给定时间,而不是在该时间过去后立即继续。

所以,虽然我不知道你想达到什么目的,但我很确定你应该改用计时器。

【讨论】:

我在玩不同类型的 Gameloops,因为我不想在逻辑和渲染完成后浪费所有 CPU(在俄罗斯方块克隆中没有太多要绘制和计算的东西)我需要一种方法来暂停我的程序。但是因为我不想开始讨论 GameLoops 是如何设计的,所以我提出了一个关于如何在特定时间内暂停代码的问题。 @haBaLeS 一般来说,如果我完全了解您的来源,我会考虑使用类似 CyclicBarrier 的东西。【参考方案9】:

在当前线程上使用Thread::join overrides 之一。您指定等待的毫秒数(和纳秒)。

【讨论】:

所以如果我错了,请指出原因,而不是默默地投反对票,谢谢 纳秒四舍五入到毫秒等待加入。

以上是关于Windows 上的 Java 准确睡眠的主要内容,如果未能解决你的问题,请参考以下文章

准确的javascript睡眠功能

如何在 C(Linux)中的 while 循环中准确睡眠?

如何使用 HealthKit 从特定日期准确获取 24 小时的睡眠数据?

如何使用 HealthKit 从特定日期准确获取 24 小时的睡眠数据?

用于内存消耗测量的python睡眠不准确

GetPwrCapabilities 在某些计算机上给了我错误的睡眠状态结果。如何获得更准确的结果?