notifyAll () количество различий в вызовах при профилировании - PullRequest
11 голосов
/ 03 мая 2011

Я реализовал простой профилировщик с 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 к операционной системе.

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

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

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

Первоначально добавлено в качестве ответа, перенесено на вопрос extraneon:

Мне кажется, я выяснил, откуда некоторые дополнительных уведомлений, все, я добавил профилирование контекста метода, в котором вызывается 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.

Ответы [ 2 ]

4 голосов
/ 03 мая 2011

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

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

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

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

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

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

1 голос
/ 03 мая 2011

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

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

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

...