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

StackOverflow https://stackoverflow.com/questions/1648551

  •  22-07-2019
  •  | 
  •  

Вопрос

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

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

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

Это было полезно?

Решение

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

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

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

Другие советы

  

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

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

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

<Ол>
  • Java всегда многопоточная. Пример: посмотрите список потоков, используемых процессом, который когда-то использовал jconsole или jvisualvm.
  • Правильность важнее, чем & производительность. " Когда я занимался оценкой проектов (много лет назад), мне приходилось объяснять & «Неверный ответ» действительно быстро по-прежнему не так. & Quot;
  • По сути, эти методы предоставляют некоторые ловушки для управления мониторами для каждого объекта, используемыми при синхронизации. В частности, если у меня есть synchronized(objectWithMonitor) в конкретном методе, я могу использовать objectWithMonitor.wait() для выдачи этого монитора (например, если мне нужен другой метод для завершения вычисления, прежде чем я смогу продолжить). В этом случае это позволит еще одному заблокированному методу дождаться продолжения работы этого монитора.

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

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

    <Ол>
  • Сначала докажи это. Если вы считаете, что основной механизм Java, такой как многопоточная корректность, оказывает серьезное влияние, есть отличный шанс, что ваша интуиция ложна. Измерьте воздействие в первую очередь. Если это серьезно, и вы знаете , что вам никогда не придется синхронизироваться на отдельном Double, рассмотрите возможность использования double вместо этого.
  • Если вы не уверены, что вам, вашему коллеге, будущему программисту по техническому обслуживанию (который может стать самим собой через год) и т. д., никогда никогда не потребуется тонкая гранулярность Тем не менее, благодаря удаленному доступу к вашим данным, исключение этих мониторов сделает ваш код менее гибким и обслуживаемым.
  • Последующие действия в ответ на вопрос об объектах на уровне объекта и явных объектах монитора:

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

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

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

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

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

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

    Все объекты в Java имеют связанные с ними мониторы. Примитивы синхронизации полезны практически для всего многопоточного кода, и его семантически очень приятно синхронизировать по объектам, к которым вы обращаетесь, а не по отдельным & Quot; Monitor & Quot; объекты.

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

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

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

    Проверить эта статья на эту тему.

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

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

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

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top