Подтвердить что ты не робот

Почему у всех объектов Java есть wait() и notify(), и это приводит к поражению производительности?

Каждая Java Object имеет методы wait() и notify() (и дополнительные варианты). Я никогда не использовал их, и я подозреваю, что многие другие нет. Почему они настолько фундаментальны, что каждый объект должен иметь их, и есть ли успех при их использовании (предположительно, какое-то состояние хранится в них)?

EDIT, чтобы подчеркнуть этот вопрос. Если у меня есть List<Double> с 100 000 элементов, то каждый Double имеет эти методы, поскольку он расширяется от Object. Но маловероятно, что все они должны знать о потоках, которые управляют List.

ИЗМЕНИТЬ отличные и полезные ответы. @Jon имеет очень хорошее сообщение в блоге, которое кристаллизовало мои чувства кишки. Я также полностью согласен с @Bob_Cross, что вы должны показать проблему с производительностью, прежде чем беспокоиться об этом. (Также как n-й закон успешных языков, если бы это был удар производительности, то Sun или кто-то его исправил).

4b9b3361

Ответ 1

Ну, это означает, что каждый объект должен иметь связанный с ним монитор. Один и тот же монитор используется для synchronized. Если вы согласны с решением о возможности синхронизации на любом объекте, то wait() и notify() больше не добавляются для каждого объекта. JVM может выделять фактический монитор лениво (я знаю,.NET делает), но должно быть какое-то пространство для хранения, чтобы сказать, какой монитор связан с объектом. По общему признанию, возможно, что это очень маленькая сумма (например, 3 байта), которая на самом деле не сохранит какую-либо память из-за заполнения остальной части служебных данных объекта - вам нужно будет посмотреть, как каждая отдельная JVM обрабатывает память, чтобы сказать наверняка.

Обратите внимание, что просто наличие дополнительных методов не влияет на производительность (кроме очень незначительно из-за того, что код явно присутствует где-то). Это не похоже на то, что каждый объект или даже каждый тип имеет свою собственную копию кода для wait() и notify(). В зависимости от того, как работают vtables, каждый тип может содержать дополнительную запись vtable для каждого унаследованного метода - но это все еще только для каждого типа, а не для каждого объекта. Это в основном будет затеряться в шуме по сравнению с основной частью хранилища, которая предназначена для самих реальных объектов.

Лично я чувствую, что и .NET, и Java допустили ошибку, связав монитор с каждым объектом - вместо этого я бы предпочел иметь явные объекты синхронизации. Я написал немного больше об этом в сообщении о перепроектировании java.lang.Object/System.Object.

Ответ 2

Почему они настолько фундаментальны, что каждый объект должен иметь их и там была поражена производительность (предположительно, какое-то состояние сохраняется в их)?

tl; dr: Они являются методами защиты от потоков, и они имеют небольшие затраты по сравнению с их значением.

Основные реалии, которые эти методы поддерживают:

  • Java всегда многопоточна. Пример: просмотрите список потоков, используемых процессом, используя jconsole или jvisualvm некоторое время.
  • Правильность важна, чем "производительность". Когда я оценивал проекты (много лет назад), мне приходилось объяснять, что "получить неправильный ответ очень быстро все еще не так".

В принципе, эти методы предоставляют некоторые из перехватов для управления мониторами на основе объектов, которые используются в синхронизации. В частности, если у меня есть synchronized(objectWithMonitor) в определенном методе, я могу использовать objectWithMonitor.wait() для вывода этого монитора (например, если мне нужен другой метод для завершения вычисления, прежде чем я смогу продолжить). В этом случае это позволит заблокировать еще один метод, ожидающий завершения этого монитора.

С другой стороны, я могу использовать objectWithMonitor.notifyAll(), чтобы потоки, ожидающие монитора, знали, что скоро я откажусь от монитора. Они фактически не могут действовать, пока я не покину синхронизированный блок.

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

  • Сначала докажите это. Если вы считаете, что есть существенное влияние на основной Java-механизм, такой как многопоточная правильность, есть отличная вероятность, что ваша интуиция ложна. Сначала измерьте воздействие. Если это серьезно, и вы знаете, что вам никогда не понадобится синхронизация на отдельном Double, рассмотрите вместо этого использование двойников.
  • Если вы не уверены, что вы, ваш сотрудник, будущий кодер обслуживания (который может быть самим собой через год) и т.д., никогда не понадобятся тонкой гранулярности вашего доступа к вашим данным, там отличная вероятность того, что снятие этих мониторов сделает ваш код менее гибким и удобным.

Последующее наблюдение в ответ на вопрос о объектах с явным монитором per-Object:

Короткий ответ: @JonSkeet: да, удаление мониторов создаст проблемы: это создаст трение. Сохранение этих мониторов в Object напоминает нам, что это всегда многопоточная система.

Встроенные мониторы объектов не сложны, но они: легко объясняются; работать предсказуемым образом; и ясны в их назначении. synchronized(this) - это четкое выражение о намерениях. Если мы вынуждаем начинающих кодеров использовать пакет concurrency исключительно, мы вводим трение. Что в этом пакете? Что такое семафор? Вилочных присоединиться?

Новичок-кодер может использовать объектные мониторы для написания приличного кода контроллера модели. synchronized, wait и notifyAll могут использоваться для реализации наивной (в смысле простой, доступной, но, возможно, неэффективной) эффективности потоков. Канонический пример - это один из этих парных разрядов (по определению OP), который может иметь один поток, задающий значение, в то время как поток AWT получает значение, чтобы поместить его в JLabel. В этом случае нет веских оснований для создания явного дополнительного объекта только для внешнего монитора.

При несколько более высоком уровне сложности эти же методы полезны в качестве метода внешнего мониторинга. В приведенном выше примере я явно сделал это (см. Фрагменты objectWithMonitor выше). Опять же, эти методы действительно удобны для создания относительно простой безопасности потоков.

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

Punchline: Используя основные методы синхронизации, вы можете использовать большую часть производительности, поддерживаемую современными многоядерными процессорами с безопасностью потоков и без больших накладных расходов.

Ответ 3

Все объекты в Java имеют связанные с ними наблюдатели. Примитивы синхронизации полезны практически во всем многопоточном коде, и его семантически очень приятно синхронизировать объекты (объекты), к которым вы обращаетесь, а не от отдельных объектов "Монитор".

Java может выделять Мониторы, связанные с объектами по мере необходимости - как это делает .NET - и в любом случае фактические накладные расходы для простого выделения (но не использования) блокировки были бы довольно маленькими.

Вкратце: очень удобно хранить объекты со своими битами поддержки потока, а также очень мало влияет на производительность.

Ответ 4

Эти методы предназначены для взаимодействия между потоками.

Отметьте эту статью по теме.

Правила для этих методов, взятые из этой статьи:

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

Надеюсь, что это поможет...