clojure.core.async/timeout
使用 System/currentTimeMillis
来计算其截止时间(见这里和这里)。虽然System/currentTimeMillis
的文档字符串没有明确提及这一点,但它对于此目的显然不合适,因为它返回由操作系统报告的当前时间。由于该时间可能会任意更改(例如,通过NTP进行校正),截止时间的计算可能最终结果不一致。这种行为是故意的,例如请参阅这个票据,其中建议尽可能将System/currentTimeMillis
转换为单调时钟。以下评论被拒绝
System.currentTimeMillis()需要返回自纪元以来的毫秒数,并应反映系统/墙钟时间的外部更改。CLOCK_MONOTONIC_COARSE不提供这一点,因此不能用于此目的。
java.util.concurrent.ScheduledExecutorService
的文档字符串对此表述相当明确
所有计划方法都接受相对延迟和周期的参数,而不是绝对时间或日期。将表示为Date的绝对时间转换为所需形式是一个简单的问题。例如,要安排在未来某个日期,您可以这样做:schedule(task, date.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS)
。但是请注意,由于网络时间同步协议、时钟漂移或其他因素,相对延迟的到期不一定与任务启用的当前日期相吻合。
由于我们在这里只关心差分,所以似乎最好改用System/nanoTime
。不幸的是,它的文档字符串
也没有清楚地表明它的属性。它没有提及这是一个单调时钟,而只是通过以下声明暗示了
此方法返回正在运行的Java虚拟机的高精度时间源当前值,以纳秒为单位。此方法只能用于测量已过时间,与任何其他系统或实时时间的概念无关。 [...] 此方法返回的值仅在计算同一Java虚拟机实例中获取的两个这样的值之间的差异时才有意义。
使用它的一个例子是OpenJDK的java.util.concurrent.ScheduledThreadPoolExecutor
实现,其截止时间计算方法以及它的getDelay
实现。
此外——如果有什么作为依据的话——StackOverflow的各种帖子也讨论了这一点,并得出相同的结论,例如这个
尽管如此,对于实现计时阻塞、间期等待、超时等,仍应首选nanoTime(),而不是currentTimeMillis(),因为后者容易受到“时间倒流”现象的影响(例如,由于服务器时间校正),即currentTimeMillis()根本不适用于测量时间间隔。更多信息请参见此答案。
另请参阅有关core.cache
的类似问题。