Java重排序对System.currentTimeMillis()有影响吗?

19

根据Java内存模型,只要执行是良好形式的,指令可以被重新排序。

因此我想知道,以下代码是否可能产生以下输出结果?

[在同一线程中的代码]

long a = System.currentTimeMillis();
long b = System.currentTimeMillis();
long c = System.currentTimeMillis();

[输出]

a == 10, b == 20, c == 15

如果不可能的话,那么JVM / 实现会采取什么措施来防止这种情况发生?


@ElliottFrisch 你好。这些确切的值被用来说明a、b和c可能不是单调递增的;不一定是10、20和15 :-P - Kurtt.Lin
3
System.currentTimeMillis() 是一个用户空间的系统调用,执行系统调用的顺序错误应该是任何系统中的一个 bug。 - Peter Lawrey
如果这些方法是相同的,无序执行它们会有什么好处?JIT和CPU并不会随机地无序执行事物,而是在明显可以提高性能的情况下执行。 - Peter Lawrey
1
我相信这篇关于“concurrency-interest”邮件列表的帖子,以及整个讨论线程都会对这里的人有兴趣。 - Marko Topolnik
1
@BenjaminGruenbaum 精度并不重要,因为总有一个时刻,一毫秒变成另外一毫秒。 - Peter Lawrey
显示剩余9条评论
2个回答

5
请查看这个问题JAVA中的指令重排和发生前关系。我认为,除非你在另一个线程中执行代码,否则任何执行结果都将与代码顺序一致。在这种情况下,即使您的字段对另一个线程可见,由于不可能按顺序处理它,因此应该是正常的。

Joseph,感谢您的快速回答。然而,我倾向于认为这3个指令可能会被无序执行,因为调用方法不是Java内存模型定义的操作,因此这3个指令之间不存在happens-before关系。如果在同一线程中int a=1+1,int b=2+2可以重新排序并执行为int b=2+2,int a=1+1,那么System.currentTimeMillis()有什么特殊之处使它们必须按顺序执行呢? - Kurtt.Lin
实际上,“currentTimeMillis”要么被视为“外部调用,所以我不知道它的作用,并且我不会重新排序它”。否则,假设重新排序者知道它的工作原理,如果这个方法纯粹是用Java实现的,那么它将需要读取一些易失性变量(系统时钟)。无论哪种情况,它都不应该被重新排序。 - Sebi
@Sebi,我喜欢你的推断;有任何参考可以证明吗? - Kurtt.Lin
@Kurtt.Lin,我刚刚检查了一下,这个方法被定义为:public static native long currentTimeMillis();(JDK6 b33中)。所以我猜它不会被重新排序,因为它是“native”方法。(这只是我的猜测,但我想不出一种确定本地方法是否可以被重新排序的方式) - Sebi
1
无论与 currentTimeMillis() 调用相关的操作是否可以被重新排序,都不重要,因为 currentTimeMillis() 的结果本身就不能保证单调递增。例如,在两次调用之间可能会进行闰秒校正或 NTP 更新。 - Holger

0
由于是用户系统调用,编译器不应在同一线程中重新排序它们。如果这不是真的,我们甚至可能会在System.out.println(独立值)中体验到重新排序效果。 我猜访问系统/操作系统时钟会在这些操作之间创建某种关系(始终针对当前线程),因此理论上存在某种依赖关系。可能,JVM考虑了这个问题,从不重新排序用户系统调用。

1
谁说currentTimeMillis是系统调用?那只是实现细节。 - usr
这是一个实现细节,但实际上当前的实现是否使用了系统资源?如果是的话,JVM 可能会对其进行审查并避免重新排序。如果默认实现不包括系统调用,则情况可能会有所不同。 - Kostas Kryptos
你是说 native 方法调用不能被重新排序吗? - Sotirios Delimanolis
不,我的意思是如果重新排序包含至少一个外部调用的调用,那么它可能会出现错误。希望JVM可以决定重新排序它完全控制的调用。但如果存在依赖于系统资源而它无法控制的情况,它怎么知道是否可以提高性能呢? - Kostas Kryptos

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