【问题标题】:Is -System.nanoTime() + System.nanoTime() guaranteed to be >= 0?-System.nanoTime() + System.nanoTime() 是否保证 >= 0?
【发布时间】:2012-01-13 16:19:17
【问题描述】:

大家好,我有一段代码如下所示:

public class Test {
    public static void main(String args[]) {
        long a = System.currentTimeMillis(); // line 1
        long b = System.currentTimeMillis(); // line 2
        assert b - a >= 0;

        long y = System.nanoTime(); // line 5
        long z = System.nanoTime(); // line 6
    }
}

所以IERS 表示下一个闰秒将在 2012 年 6 月 30th 11:59.9 之后立即发生。

我想知道我是否正确地说如果第 1 行在 30th June 2012 11:59.9 转为 1st 2012 年 7 月 00:00.0,

并且第 2 行在第 1 行之后 0.1 秒运行,

b - a 的结果可能是否定? (-900 毫秒)

如果是这样的话,如果第 5 行在 30th June 2012 11:59.9 转为 1 后 0.9 秒运行,是不是真的st 2012 年 7 月 00:00.0,

第 6 行在第 5 行之后 0.1 秒运行,

z - y 的结果可能是否定? (-900,000,000 纳秒?)

【问题讨论】:

  • 您是在问闰秒是否会改变单调递增时钟的工作方式?你预计会发生什么?
  • 为什么这些是负面的?你找到了让时光倒流的方法吗?除非在这两个调用之间调整系统时钟,否则时间将为正或空(取决于系统时钟的准确性)
  • @EdwardThomson 是的,我想知道闰秒是否会影响 System.currentTimeMillis() 的结果,因为每次闰秒发生时,POSIX 时间向后一秒跨度>
  • @JBNizet 因为只要出现闰秒,POSIX 时间就会回到过去,如表中所示插入 UTC 闰秒时跨午夜的 Unix 时间:en.wikipedia.org/wiki/Unix_time
  • 那么为什么不问那个问题,而不是希望人们点击链接和/或记住闰秒的时间安排。 ;)

标签: java time


【解决方案1】:

System.nanoTime 应该 单调递增——如果你有两次调用它,A 和 B,以及 Ahappens-before B,然后是 A <= B .但在实践中,您实际上可以观察到nanoTime“倒退”。

nanoTime 由 CPU 上的内部计数器确定,其开始时间基本上是任意的(这就是为什么它不能用于确定挂钟时间的原因)。这可能会在多核环境中引起问题,因为一个内核的内部定时器可能与另一个内核的起始点不同。 Hotspot 试图弥补这一点,但并不总是成功,因此您实际上可以看到 nanoTime 在某些情况下倒退。

并发兴趣邮件列表上有一个recent discussion 与此有关。特别参见链接到this bug report 的this email 和this email 讨论解决方法(这似乎不起作用,尽管我不确定为什么)。错误报告有相当多的细节。

【讨论】:

    【解决方案2】:

    如果第 1 行在 2012 年 6 月 30 日 11:59.9 转为 2012 年 7 月 1 日 00:00.0 之后的 0.9 秒运行,我是否正确,

    如果不调整时钟,则30th June 2012 11:59.9后0.9秒为1st July 2012 00:00.8

    b - a 的结果是否定的?

    currentTimeMillis() 是自 1970 年以来的时间(以毫秒为单位)。它不会在一天开始时重置。或者你生命中的任何时候。

    z - y 的结果是否定的?

    nanoTime() 也不是一天开始以来的时间。在许多 JVM/OS 上,它是自 CPU 上次重置以来的纳秒数。


    并非所有操作系统都提供相同的分辨率。例如RHEL/Centos 5.x 只提供微秒级的分辨率。这意味着您可以连续多次调用相同的值(到微秒)

    long a = System.currentTimeMillis(); // line 1
    long b = System.currentTimeMillis(); // line 2
    assert b - a >= 0;
    

    只要通过向后转动时间来更正时间,它就会倒退。例如通过 NTP。

    long y = System.nanoTime(); // line 5
    long z = System.nanoTime(); // line 6
    

    这将在具有多个套接字的系统上倒退,这些套接字无法纠正不同套接字中时间戳计数器的差异。例如如果您使用的是 Windows XP 并且有两个 Socket,那么您可以看到差异会向前或向后跳跃 4,000,000,因为它会在套接字之间切换线程。

    【讨论】:

    • +1 表示套接字。这显然违反了 nanoTime() 的定义。我希望 Windows XP 是唯一能做到这一点的系统。
    • Windows Vista/7、Solaris 和受支持的 Linux 版本都可以。我相信 Linux 曾经有过这个错误。
    • @PeterLawrey 但是时钟不是在 2012 年 6 月 30 日结束时为 2012 年闰秒调整了吗?如果是这样,那不是意味着我们会得到否定的结果吗?
    • 这意味着当 NTP 启动时(因为大多数系统实际上并不支持 UTC 闰秒),它可能会看到机器上的时间快 1 秒。如果您要立即纠正这一点,您会看到您建议的时间跳跃。但是大多数系统不这样做,而是在一段时间内说一分钟,它增加了 59 秒(而不是 60 秒)而不是时间倒退,它会稍微慢一点直到它正确。同样的事情恰好向前移动了 1 秒。更大的修正确实会导致时间跳跃。 manpagez.com/man/8/ntpdate
    • @Pacerier NTP 与客户端通信闰秒指示器,默认情况下在 Linux 上使用 ntpd,这会导致后退一秒,而不是轮询 NTP 服务器并看到有一个错误。所以 128 毫秒的步长阈值不是一个因素,除非您尝试使用 Google 或 AWS “涂抹”闰秒之类的东西,其中 NTP 客户端是哑的,但服务器已修补以缩短时间。然后,轮询间隔需要足够小,不超过 128 毫秒。这就是为什么他们会在一整天而不是像 UTC-SLS 那样持续 1000 秒。
    【解决方案3】:

    不,你错了。因为这不是当前时间的毫秒部分,而是从 1970 年过去的总毫秒数。

    它们可能是相同的,但以后不会比以前少。但是,如果 NTP 守护进程正在执行它的工作,如果系统时钟在某个时刻已调整,则可能会发生这种情况。

    nanoTime 是更可靠的方式,因为它不依赖于系统时钟,也不应该通过时钟调整来改变。

    【讨论】:

    • 现在是 POSIX 时间,对吧?因为如果是 POSIX 时间,它不是从 1970 年过去的总毫秒数。闰秒导致 POSIX 时间倒退,所以如果它基于 POSIX 时间,它也会倒退跨度>
    • 闰秒在校正后不会导致时间倒退。相反,它会导致在一段时间(AFAIK 分钟)内添加额外的秒数,从而使时钟看起来比正常时间稍慢,例如与 nanoTime() 相比。
    • @PeterLawrey 对不起,我在这里感到困惑。 en.wikipedia.org/wiki/Unix_time#leapsecondinserted 的图表不是显示 UNIX 时间 915 148 800.75 倒退到 915 148 800.00 吗?
    • 我怀疑大多数 UNIX 系统是strictly conforming POSIX.1 systems at the end of 1998 但是,您可能是对的,闰秒的处理方式与其他时间校正不同。 AFAIK,大多数系统默认关闭闰秒并允许 NTP 更正时间,但我可能错了。
    【解决方案4】:

    我对 wiki 页面的阅读和你的一样:currentTimeMillis() 可以由于闰秒而倒退。

    (他们为什么要把这个天文学的精细问题带入民用时间?没有平民关心太阳正午是否偏离几秒钟;实际上没有人以当地时间开始;同一时区的人可以观察到太阳正午时差1 小时。在一个没有时区的大国,时差可能是小时。)

    【讨论】:

    • 操作系统的任何时钟同步都可能导致挂钟向任何方向跳跃,闰秒就是其中之一 AFAIK
    【解决方案5】:

    -System.nanoTime() + System.nanoTime() 保证>= 0?

    是的。这是一个计时器,不是任何绝对时间,根据其文档,它返回最精确的可用系统计时器的当前值,以纳秒为单位。返回的值表示自某个固定但任意时间以来的纳秒。 自某个固定时间以来的时间不会倒退(尽管 292 年后差异会溢出,但这几乎不是一个实际问题。另外,正如 Peter Lawrey 指出的那样, Windows XP has a bug that breaks nanotime's guarantees)。

    System.currentTimeMillis() 完全不同。它返回从计算机时钟获取的绝对时间(自 1970 年以来的毫秒数),可以随时调整。

    【讨论】:

    • 你的意思是说即使通过NTP调整时间,System.nanoTime()的时间也是稳定的?
    • @Pacerier:是的,没错。这就是拥有currentTimeMillis() 的想法——如果nanoTime() 的行为相似,那么currentTImeMillis() 将是多余的(只需将纳米时间除以10e6)。
    • @JoonasPulakka:虽然我同意你关于 nanoTime() 的观点,但我认为你不能在这里使用冗余作为参数,因为 nanoTime() 是在 Java 5 中添加的。(即使这使得currentTimeMillis() 多余的,即使保持反向兼容,它也会被保留。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-05
    • 1970-01-01
    • 2016-05-25
    相关资源
    最近更新 更多