22

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

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

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

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

4

9 回答 9

18

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

Windows 下 Thread.sleep() 的错误

如果时间对您的应用程序至关重要,那么解决这些错误的一种不雅但实用的方法是让一个守护线程在您的应用程序的整个持续时间内运行,它只是休眠大量的质数毫秒(Long.MAX_VALUE 会这样做)。这样,每次调用应用程序都会设置一次中断周期,从而最大限度地减少对系统时钟的影响,并将睡眠粒度设置为 1ms,即使默认中断周期不是 15ms。

该页面还提到它会导致 Windows 系统范围内的更改,这可能会导致用户的时钟由于此错误而快速运行。

编辑

有关这方面的更多信息,请参见 此处以及来自 Sun的相关错误报告。

于 2009-05-05T11:30:00.700 回答
12

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

看看:LockSupport.parkNanos

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

于 2009-10-28T19:02:04.363 回答
9

不幸的是,从 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,这会给你非常准确的睡眠

于 2010-06-07T03:28:27.263 回答
4

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) 本机库的准系统结构不应该是遥不可及的。

于 2009-05-05T11:15:46.147 回答
4

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

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

于 2009-05-05T10:04:44.477 回答
3

Long.MAX_VALUE hack 是有效的解决方案。

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

于 2009-05-15T05:10:44.183 回答
1

您可以尝试使用新的并发库。就像是:

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 毫秒之间休眠。

于 2009-05-05T19:39:26.110 回答
1

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

于 2009-05-05T09:43:49.037 回答
0

听起来您需要实时 Java的实现。

于 2009-05-05T11:21:22.220 回答