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 准确睡眠的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 HealthKit 从特定日期准确获取 24 小时的睡眠数据?