【问题标题】:Multithreading with Java nanoTime使用 Java nanoTime 进行多线程
【发布时间】:2013-11-19 22:06:18
【问题描述】:

我目前正在使用 Java 编写多线程程序。在某些时候,不同的线程会记录他们的操作,我为此使用nanoTime。每个线程都有自己的日志文件,最后我将它们合并并根据它们的时间(nanoTime)对它们进行排序以查看发生了什么。问题是我有类似的错误行为,其中xvolatile 变量:

// logged by thead1
x = true // done at time 0000, time computed & logged after x = true was done

// no events in between

// logged by thread2
read x // reads false while time before reading is 0001

所以在我看来nanoTime 并没有真正正确地计时。 在nanoTime 文档中写道:

此方法返回的值只有在 在同一实例中获得的两个此类值之间的差异 Java 虚拟机的,被计算出来。

是否有可能由同一进程创建的线程在不同的 JVM 中执行?这可以解释nanoTime 的错误行为,但仍然没有太大意义。有什么想法吗?

【问题讨论】:

  • "同一进程创建的线程是否有可能在不同的 JVM 中执行?"除非您先明确创建一个单独的 jvm,否则不会。你上面的代码是在同一个线程中吗?
  • 我明白了。没有x = true 是在与read x 不同的线程中完成的
  • 向我们展示更多您的代码。仅用两行代码很难找到潜在的同步错误。
  • @JBNizet 代码非常大,可以提出一个 SO 问题。我只是试图减少它......:s。所以你无法从我描述的行为中知道为什么会发生这种情况?
  • 明确地说,x 被声明为 volatile?如果是这样,我敢打赌你还有其他同步问题。

标签: java multithreading concurrency milliseconds nanotime


【解决方案1】:

这完全取决于操作系统,但如果您有一个 CPU 插槽,您将无法使用 nanoTime 看到任何错误。如果您使用 Windows 7+ 或最新版本的 Linux,nanoTime 会更正套接字之间的差异。顺便说一句,如果你有一个多插槽 XP 盒子,你可以看到 nanoTime 前后跳跃几毫秒。 (简而言之,不要在多插槽机器上使用 XP 并期望得到好的结果)

另请注意,某些操作系统只有微秒级分辨率。这意味着您可以在线程之间执行许多操作,并且它们似乎都具有相同的时间戳。解决方案是使用具有更高分辨率计时器的操作系统。这不涉及更改您的代码甚至您的 JVM。

同一个进程创建的线程是否有可能在不同的JVM中执行?

我不知道有什么方法可以做到这一点,我也想不出这很重要的原因。无论您使用的是哪个线程、JVM 或进程,都会进行相同的系统调用。

这可以解释 nanoTime 的错误行为,但仍然没有太大意义。有什么想法吗?

您的操作系统上的 nanoTime() 调用很可能只有微秒级的分辨率。

read x // 在读取前的时间为 0001 时读取为 false

如果您的时间优于微秒,那么每次调用 nanoTime() 都可能不同。通过缓存更新 volatile 变量至少需要 75 个时钟周期或约 20 纳秒。没有什么是即时的,我已经看到线程之间的延迟接近 100 纳秒,因为线程之间的微不足道的更新。

【讨论】:

  • 我没有理由怀疑,因为我知道你非常了解这些低级函数。但是为什么 nanoTime 的 javadoc 没有提到这一点?它明确地说: Java 虚拟机实例中此方法的所有调用都使用相同的来源;其他虚拟机实例可能使用不同的来源。,这与您在回答中谈到的多插槽问题相矛盾。
  • @JBNizet 我认为这是有意义的;这不是必需的。我不知道同一台机器上的不同进程有不同来源的任何操作系统/JVM。这是一个额外的复杂性,我看不到操作系统或 JVM 添加的任何价值。
  • @JBNizet 另一方面,在 Windows XP 上,一个线程可以看到时间向前和向后跳跃。这是我见过的唯一一个问题示例。
  • 感谢您的回答。请问什么是“多路XP”?这是否意味着多处理器?或者如何确定我的处理器是否为多路处理器?关于微秒分辨率,我认为我的处理器具有更好的时间分辨率,因为所有时间都不同(即使两个后续的nanoTime 调用给我不同的数字)。在我的示例中,数字 000001 是任意的。通常相差1000ns以上。
  • 我记得读过关于nanoTime 是通过在 XP 中调用 QueryPerformanceCounter 来实现的。
【解决方案2】:

我不知道期望 nanoTime 允许您以这种方式对发生在不同线程上的事件进行排序是否有效。我会重新考虑您的设计,每个线程都有一个单独的日志文件,您可以根据时间将其合并在一起。如果您使用的是 log4j,我会让它们写入同一个日志。

如果您正在构建自己的日志记录实现,我会让他们将消息写入java.util.concurrent.LinkedBlockingQueue(不会发生争用,我怀疑您通过让每个线程都有自己的日志来避免这种情况) ,并有另一个线程负责定期排出队列中可用的任何消息并将它们写入文件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 2012-08-04
    • 2015-09-26
    • 1970-01-01
    • 2016-08-22
    相关资源
    最近更新 更多