11

wait()とで呼び出しを表示するために、JVMTI を使用して単純なプロファイラーを実装しましたnotifyAll()。テストケースとして、私はを使用しています。Oracle の生産者消費者の例。次の 3 つのイベントがあります。

  • notifyAll() が呼び出されます
  • wait() が呼び出される
  • wait() が残っている

wait()イベントMonitorEnterMonitorExit. _ 名前を持つメソッドが終了すると、notifyAll()呼び出しがプロファイリングされますnotifyAll

これで、次の結果が得られました。1 つ目はプロファイラー自体からのもので、2 つ目は適切なステートメントを配置した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

しかし、これらの外部呼び出しがなくても、printf デバッグに表示されない notifyAll 呼び出しがたくさんあります。

4

2 に答える 2

4

オラクルが提供するProducer-Consumerの例とあなたの出力 (プロファイラーと Java プログラム)の分析に時間を費やしました。いくつかの予期しないもの以外に、出力にいくつかの奇妙なことがありますnotifyAll()

  1. wait() メソッドが 4 回実行されることを期待する必要があります (Stringプロデューサーによって操作される配列には 4 つの要素があります)。プロファイラーの結果は、3 回しか実行されなかったことを示しています。

  2. もう 1 つの非常に奇妙な点は、プロファイラーの出力でのスレッドの番号付けです。この例には 2 つのスレッドがありますが、プロファイラーはすべてのコードを 1 つのスレッドで実行しThread-1ます。Thread-0notifyAll()

  3. 提供されているサンプル コードは、同時実行の観点と言語の観点から正しくプログラミングされていwait()ますnotifyAll()。モニターを確実に制御するために同期されたメソッドになっています。待機状態はwhile、通知がメソッドの最後に正しく配置されたループ内にあります。しかし、catch (InterruptedException e)ブロックが空であることに気付きました。つまり、待機中のスレッドが中断された場合、notifyAll()メソッドが実行されます。これは、いくつかの予期しないnotifyAll().

結論として、コードを変更して追加のテストを実行しなければ、問題の原因を突き止めることは容易ではありません。

余談ですが、JVMTI で遊んでみたい好奇心旺盛な方のために、JVMTI を使用したデバッグおよびプロファイリング エージェントの作成のリンクを残しておきます。

于 2011-05-03T16:22:06.753 に答える
1

コードに競合状態がある場合、プロファイラーはコードの速度を低下させて、コードのエラーを表示または非表示にすることができます。(競合状態を表示するためだけに、プロファイラーでプログラムを実行するのが好きです。)

notifyAll()はwait()スレッドにのみ通知するため、notifyAll()の後にwait()を呼び出すと、通知が失われる可能性があります。つまり、ステートレスであり、以前に通知を呼び出したことを知りません。

アプリケーションの速度を落とすと、wait()が開始するまでnotifyAll()を遅らせることができます。

于 2011-05-03T11:58:02.913 に答える