NotifyAll() количество разностей вызовов при профилировании

Я реализовал простой профайлер с JVMTI для отображения вызова на wait() и notifyAll(). В качестве тестового примера я использую. пример использования производителя Oracle. У меня следующие три события:

  • notifyAll() вызывается Вызывается
  • wait()
  • wait() остается

Вызов wait() и когда он оставил его профилированным с помощью событий MonitorEnter и MonitorExit. Вызов 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 на операционную систему.

A notifyAll() запросить отправку в операционную систему, и отправляется ложноположительный ответ, где кажется, что запрос был успешным. Поскольку notifyAll регистрируется путем вызова метода профилирования вместо MonitorEnter, он может объяснить, почему этого не происходит с ожиданием.

Я забыл сказать, что я не запускал программы отдельно, оба журнала имеют одно и то же выполнение.

Дополнительная информация

Первоначально добавленный в качестве ответа, переместился на вопрос extraneon:

Я думаю, что я узнал, откуда some дополнительного 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

Но даже без этих внешних вызовов есть pleety notifyВсе звонки, которые не отображаются в отладке printf.

Ответ 1

Я потратил некоторое время на анализ Producer-Consumer, представленный Oracle и вашим продуктом (профилировщик и Java-программа). На ваших выходах есть некоторые странные вещи, кроме нескольких неожиданных notifyAll():

  • мы должны ожидать, что метод wait() будет выполняться 4 раза (массив String, управляемый производителем, имеет 4 элемента). Результат вашего профилировщика показывает, что он выполнен только три раза.

  • Еще одна вещь, которая довольно странная, - это нумерация потоков на выходе профилировщика. В примере два потока, однако ваш профилировщик выполняет весь код в одном потоке, т.е. Thread-1, а Thread-0 выполняет только notifyAll().

  • Приведенный пример кода правильно запрограммирован с точки зрения параллелизма и языка: wait() и notifyAll() синхронизированы для обеспечения контроля над монитором; условие ожидания находится в цикле while с уведомлением, правильно помещенным в конце методов. Тем не менее, я заметил, что блок catch (InterruptedException e) пуст, а это означает, что если ожидающий поток прерывается, будет выполнен метод notifyAll(). Это может быть причиной нескольких неожиданных notifyAll().

В заключение, не выполняя некоторые изменения в коде и выполняя некоторые дополнительные тесты, будет непросто выяснить, откуда эта проблема.

В качестве дополнительной заметки я оставлю эту ссылку Создание агента отладки и профилирования с помощью JVMTI для любознательных, которые хотят играть с JVMTI.

Ответ 2

Если у вас есть условие гонки в вашем коде, профайлер может замедлить код, достаточный для того, чтобы показать или скрыть ошибку в вашем коде. (Мне нравится запускать свою программу в профилировщике, чтобы показать условия гонки).

Как notifyAll() будет только уведомлять wait() о потоках, вызывая wait() после того, как notifyAll() может привести к отсутствию уведомления. то есть его апатридов, он не знает, что вы вызвали уведомление раньше.

Если вы замедляете свое приложение, notifyAll() может быть отложено до тех пор, пока не запустится wait().