11

我已经使用 JVMTI 实现了一个简单的分析器来显示在wait()和上的调用notifyAll()。作为测试用例,我正在使用。Oracle 的生产者消费者示例。我有以下三个事件:

  • notifyAll() 被调用
  • wait() 被调用
  • 等待()离开

wait()调用及其离开时使用事件MonitorEnterMonitorExit. 退出具有名称的notifyAll()方法时会分析调用。notifyAll

现在我有以下结果,第一个来自分析器本身,第二个来自 Java,我在其中放置了适当的System.out.println语句。

    // Profiler:
    Thread-1 invoked notifyAll()
    Thread-0 invoked notifyAll()
    Thread-0 invoked notifyAll()
    Thread-0 invoked notifyAll()
    Thread-0 invoked notifyAll()
    Thread-0 invoked notifyAll()
    Thread-1 invoked notifyAll()
    Thread-1 invoked notifyAll()
    Thread-1 invoked notifyAll()
    Thread-1 invoked notifyAll()
    Thread-1 invoked notifyAll()
    Thread-1 invoked notifyAll()
    Thread-1 invoked notifyAll()
    Thread-1 invoked wait()
    Thread-1 left wait()
    Thread-1 invoked notifyAll()
    Thread-1 invoked wait()
    Thread-1 left wait()
    Thread-1 invoked notifyAll()
    Thread-1 invoked wait()
    Thread-1 left wait()
    Thread-1 invoked notifyAll()

    // Java:
    Thread-0 invoked notifyAll()
    Thread-1 invoked notifyAll()
    Thread-0 invoked notifyAll()
    Thread-1 invoked notifyAll()
    Thread-0 invoked notifyAll()
    Thread-1 invoked wait()
    Thread-1 invoked notifyAll()
    Thread-0 invoked notifyAll()
    Thread-1 invoked wait()
    Thread-1 invoked notifyAll()
    Thread-0 invoked notifyAll()
    Thread-1 invoked wait()
    Thread-1 invoked notifyAll()

有人解释这种差异的来源吗?notifyAll()被调用了这么多次。有人告诉我这可能是由于 Java 对操作系统的请求的误报响应造成的。

它向操作系统发送了一个notifyAll()请求并发送了一个误报响应,看起来请求成功了。由于notifyAll是通过分析方法调用而不是MonitorEnter它来记录的,因此可以解释为什么等待不会发生这种情况。

我忘了说,我没有单独运行程序,两个日志都来自同一个执行。

附加信息

最初作为答案添加,由 extraneon 移至问题:

我想我发现了一些额外的 notifyAll 来自哪里,我添加了调用 notifyAll 的方法上下文的分析:

723519: Thread-1 invoked notifyAll() in Consumer.take
3763279: Thread-0 invoked notifyAll() in Producer.put
4799016: Thread-0 invoked notifyAll() in Producer.put
6744322: Thread-0 invoked notifyAll() in Producer.put
8450221: Thread-0 invoked notifyAll() in Producer.put
10108959: Thread-0 invoked notifyAll() in Producer.put
39278140: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading
40725024: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading
42003869: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading
58448450: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading
60236308: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading
61601587: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading
70489811: Thread-1 invoked notifyAll() in Consumer.take
75068409: Thread-1 invoked wait() in Drop.take
75726202: Thread-1 left wait() in Drop.take
77035733: Thread-1 invoked notifyAll() in Consumer.take
81264978: Thread-1 invoked notifyAll() in Consumer.take
85810491: Thread-1 invoked wait() in Drop.take
86477385: Thread-1 left wait() in Drop.take
87775126: Thread-1 invoked notifyAll() in Consumer.take

但是即使没有这些外部调用,也有很多 notifyAll 调用不会出现在 printf 调试中。

4

2 回答 2

4

我花了一些时间分析 Oracle 提供的Producer-Consumer示例和您的输出(分析器和 Java 程序)。除了几个意外之外,您的输出中还有一些奇怪的东西notifyAll()

  1. 我们应该期望 wait() 方法执行 4 次(String生产者操作的数组有 4 个元素)。您的分析器的结果显示它只执行了 3 次。

  2. 另一件很奇怪的事情是分析器输出中的线程编号。该示例有两个线程,但是您的分析器在一个线程中执行所有代码,即Thread-1,而Thread-0只是执行notifyAll()

  3. 提供的示例代码从并发角度和语言角度正确编程:wait()并且notifyAll()采用同步方法以确保对监视器的控制;等待条件在while循环内,通知正确放置在方法的末尾。但是,我注意到该catch (InterruptedException e)块是空的,这意味着如果正在等待的线程被中断,该notifyAll()方法将被执行。这可能是几个意想不到的原因notifyAll()

总之,如果不对代码进行一些修改并进行一些额外的测试,就很难找出问题的根源。

As a side note, I'll leave this link Creating a Debugging and Profiling Agent with JVMTI for the curious ones that want to play with JVMTI.

于 2011-05-03T16:22:06.753 回答
1

如果您的代码中有竞争条件,则分析器可以减慢代码速度,以显示或隐藏代码中的错误。(我喜欢在分析器中运行我的程序,只是为了显示竞争条件。)

由于 notifyAll() 只会通知 wait() 线程,在 notifyAll() 之后调用 wait() 很可能导致错过通知。即它的无状态,它不知道你之前调用过通知。

如果您减慢应用程序的速度,则 notifyAll() 可能会延迟到 wait() 启动之后。

于 2011-05-03T11:58:02.913 回答