-System.nanoTime() + System.nanoTime()保证大于等于0吗?

16

大家好,我有一段代码长这个样子:

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月30日11:59.9之后立即发生。

我想知道,如果在2012年6月30日11:59.9之后的0.9秒运行第1行代码,是否正确地说,在2012年7月1日00:00.0时运行第2行代码时,b - a 的结果可能是负的?(-900 毫秒)

如果是这种情况,是否正确地说,如果在2012年6月30日11:59.9之后的0.9秒运行第5行代码,而在第5行代码之后的0.1秒运行第6行代码,z-y 的结果可能是负的?(-900,000,000 纳秒?)


你是在问闰秒是否会改变单调递增时钟的工作方式?你期望会发生什么? - Edward Thomson
这些为什么会是负数?你找到了回到过去的方法吗?除非在这两个调用之间调整了系统时钟,否则时间将为正或为空(取决于系统时钟的精度)。 - JB Nizet
@EdwardThomson,我在想一个闰秒是否会影响System.currentTimeMillis()的结果,因为每次出现闰秒时POSIX时间都会倒退一秒。 - Pacerier
2
@JBNizet 因为 POSIX 时间在每次插入闰秒时会倒退,如表格所示 Unix time across midnight when a UTC leap second is inserted :http://en.wikipedia.org/wiki/Unix_time - Pacerier
2
那为什么不直接问“那个”问题,而不是希望人们点击链接或者记住何时安排闰秒呢? ;) - Brian Roach
@BrianRoach 我的意思是,这本来就是我想问的问题,我以为选择一个如此明确的日期——2012年6月30日11:59.9——是很明显的。 - Pacerier
5个回答

12

System.nanoTime应该是单调递增的——如果你对它进行两次调用,AB,并且A发生在B之前,那么A <= B。但在实际情况中,你实际上可以观察到nanoTime向后走。

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

最近在并发兴趣邮件列表上有一次讨论。特别要看一下链接到的这封邮件,它指向了这个错误报告,还有这封邮件,它讨论了一个解决方法(尽管似乎并不起作用,不过我不确定为什么)。这个错误报告有相当多的细节。

3
如果在2012年6月30日11:59.9之后0.9秒运行第一行,则会变成2012年7月1日00:00.0。
如果时钟未调整,则在“2012年6月30日11:59.9”之后的0.9秒为“2012年7月1日00:00.8”。
b-a的结果将是负数吗?
currentTimeMillis()是自1970年以来的毫秒数。它不会在一天开始时或任何时候重置。也不会在您的有生之年中的任何时间重置。
z-y的结果会是负数吗?
nanoTime()也不是从一天开始的时间。在许多JVM /操作系统上,它是自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 并具有两个插座,则可以看到随着线程在插座之间切换,差异会向前或向后跳动 4,000,000。


加1分,这显然是违反了nanoTime()的定义。我希望Windows XP是唯一一个这样做的系统。 - Joonas Pulakka
2
Windows Vista/7、Solaris和支持的Linux版本都可以。我相信Linux以前曾经有过这个bug。 - Peter Lawrey
1
@PeterLawrey 但是时钟不是在2012年6月30日结束时调整的吗?如果是这样,那岂不是意味着我们会得到一个负数结果? - Pacerier
这意味着当NTP运行时(因为大多数系统实际上不支持UTC闰秒),它可能会看到机器上的时间比实际时间快1秒。如果您立即进行纠正,您将会看到一个时间跳跃,就像您所建议的那样。然而,大多数系统不会这样做,而是在一定时间内,比如一分钟,添加59秒(而不是60秒)。时间不会倒退,而是略微变慢直到时间正确。向前移动1秒也会发生同样的事情。更大的更正确确实会导致时间跳跃。 http://www.manpagez.com/man/8/ntpdate/ - Peter Lawrey
1
@Pacerier NTP向客户端通信闰秒指示器,在Linux上默认使用ntpd,这会导致时间倒退一秒,而不是轮询NTP服务器并发现错误。因此,除非您尝试使用类似Google或AWS的“涂抹”闰秒,其中NTP客户端是愚蠢的,但服务器已经修补以调整时间,否则128毫秒的步长阈值不是一个因素。然后,轮询间隔需要足够小,以便不超过128毫秒。这就是为什么他们在整个日子里涂抹,而不是像UTC-SLS那样的1000秒。 - erickson
显示剩余2条评论

2

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

它们可能相同,但后者不会早于前者。然而,如果NTP守护程序完成其工作,如果系统时钟在某个时刻已被调整,则可能发生这种情况。

nanoTime是更可靠的方法,因为它不依赖于系统时钟,并且不应由时钟调整更改。


1
这是POSIX时间吗?因为如果是POSIX时间,它并不是从1970年以来经过的总毫秒数。闰秒会导致POSIX时间倒退,因此如果基于POSIX时间,它也会倒退。 - Pacerier
闰秒的纠正不会导致时间倒流。相反,它会在一段时间内(据我所知是分钟)增加一秒钟,使得时钟看起来比正常稍微慢一些,例如与nanoTime()相比。 - Peter Lawrey
@PeterLawrey 对不起,我在这里有点困惑。难道网站http://en.wikipedia.org/wiki/Unix_time#leapsecondinserted上的图表不是显示UNIX时间"915 148 800.75"向后退回到“915 148 800.00”吗? - Pacerier
我怀疑大多数UNIX系统在1998年底并不是“严格符合POSIX.1系统”。然而,你可能是对的,闰秒与其他时间校正的处理方式不同。据我所知,大多数系统默认关闭闰秒,并允许NTP校正时间,但我可能对此有所错误。 - Peter Lawrey

1
-System.nanoTime() + System.nanoTime()保证>= 0吗?
是的。它是一个计时器,不是任何绝对时间,并根据其文档,它“以纳秒为单位返回可用系统计时器中最精确的当前值。返回的值表示自某个固定但任意时间以来的纳秒数。”自某个固定时间以来的时间不会倒退(尽管在292年后差异将溢出,但这几乎不是实际问题。此外,正如Peter Lawrey指出的那样,Windows XP存在一个破坏nanotime保证的错误)。
System.currentTimeMillis()完全不同。它返回绝对时间(1970年以来的毫秒数),该时间从计算机的时钟获取,可以随时调整。

你的意思是即使通过NTP调整了时间,System.nanoTime()的时间仍然稳定吗? - Pacerier
@Pacerier:是的,完全正确。这就是拥有currentTimeMillis()的想法 - 如果nanoTime()的行为类似,那么currentTImeMillis()将是多余的(只需将nanotime除以10e6即可)。 - Joonas Pulakka
1
虽然我同意你关于nanoTime()的观点,但我认为你不能将冗余作为一个论据,因为nanoTime()是在Java 5中添加的。(即使这使得currentTimeMillis()变得多余,它也会被保留下来,即使是为了保持向后兼容性。) - Edward Thomson

1

我对维基页面的阅读与你相同:由于闰秒,currentTimeMillis() 可能会倒退。

(为什么要将这个精细的天文问题引入到民用时间中呢?没有人关心太阳正午是否偏差几秒钟;实际上,根本没有人使用本地时间;同一时区的人们可以观察到太阳正午相差1小时。在一个没有时区的大国中,这种差异可能是几个小时。)


任何由操作系统进行的时钟同步都可能导致挂钟跳跃,闰秒就是其中之一,据我所知。 - Viktor Mukhachev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接