Java: notify () против notifyAll () снова и снова - PullRequest
347 голосов
/ 31 августа 2008

Если один из Googles для «разницы между notify() и notifyAll()», то появится много объяснений (оставляя в стороне абзацы javadoc). Все сводится к числу ожидающих потоков: один в notify() и все в notifyAll().

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

Что такое полезная разница между notify () и notifyAll () тогда? Я что-то упустил?

Ответы [ 26 ]

1 голос
/ 22 июля 2014

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

1 голос
/ 06 сентября 2013

Я хотел бы упомянуть, что объясняется в Java Concurrency на практике:

Первый пункт, будь то Notify или NotifyAll?

It will be NotifyAll, and reason is that it will save from signall hijacking.

Если два потока A и B ожидают разных предикатов условия вызывается очередь с таким же условием и уведомлять, тогда это до JVM какую нить JVM уведомит.

Теперь, если уведомление предназначено для потока A, а JVM уведомляет поток B, тогда поток B проснется и увидит, что это уведомление бесполезно, поэтому это будет ждать снова. И Тема А никогда не узнает об этом пропущенный сигнал, и кто-то похитил его уведомление.

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

Эту проблему можно решить с помощью объекта условия явной блокировки Lock, представленного в jdk 5, поскольку он обеспечивает различное ожидание для каждого предиката условия. Здесь он будет вести себя правильно и не будет проблем с производительностью, поскольку вызовет сигнал и убедится, что только один поток ожидает этого условия

0 голосов
/ 14 декабря 2009

Просыпаться все не имеет большого значения здесь. ждать уведомления и уведомления, все они ставятся после владения монитором объекта. Если поток находится в стадии ожидания и вызывается notify, этот поток займет блокировку, и никакой другой поток в этой точке не сможет принять эту блокировку. Таким образом, одновременный доступ не может иметь место вообще. Насколько мне известно, любой вызов wait и notifyall можно сделать только после взятия блокировки на объекте. Поправь меня, если я ошибаюсь.

0 голосов
/ 28 января 2017

Хотя есть несколько убедительных ответов выше, я удивлен тем количеством недоразумений и недоразумений, которые я прочитал. Это, вероятно, подтверждает идею о том, что следует как можно больше использовать java.util.concurrent вместо того, чтобы пытаться писать собственный неработающий параллельный код. Вернемся к вопросу: подведем итог: сегодня лучше избегать уведомления () во всех ситуациях из-за проблемы с пробуждением. Любой, кто не понимает этого, не должен иметь права писать критически важный код параллелизма. Если вас беспокоит проблема скотоводства, один из безопасных способов добиться пробуждения одной нитью за раз: 1. Создайте явную очередь ожидания для ожидающих потоков; 2. Пусть каждый поток в очереди ожидает своего предшественника; 3. Пусть каждый поток вызывает notifyAll () после завершения. Или вы можете использовать Java.util.concurrent. *, Который уже реализовал это.

0 голосов
/ 21 апреля 2016

Когда вы вызываете wait () для «объекта» (ожидая, что блокировка объекта получена), интерн это снимет блокировку с этого объекта и поможет другим потокам заблокировать этот «объект», в этом сценарии будет более 1 потока, ожидающего «ресурс / объект» (учитывая, что другие потоки также выдавали ожидание для того же вышеуказанного объекта, и вниз по пути будет поток, который заполняет ресурс / объект и вызывает notify / notifyAll) ,

Здесь, когда вы отправляете уведомление об одном и том же объекте (с той же / другой стороны процесса / кода), это освобождает заблокированный и ожидающий один поток (не все ожидающие потоки - этот освобожденный поток будет выбран JVM Thread Scheduler и весь процесс получения блокировки на объекте такой же, как и в обычном режиме).

Если у вас есть только один поток, который будет совместно использовать / работать с этим объектом, то можно использовать только метод notify () в вашей реализации wait-notify.

если вы находитесь в ситуации, когда более одного потока читает и записывает ресурсы / объекты на основе вашей бизнес-логики, тогда вам следует обратиться к notifyAll ()

теперь я смотрю, как именно jvm идентифицирует и прерывает ожидающий поток, когда мы запускаем notify () для объекта ...

0 голосов
/ 11 октября 2015

Подводя итог прекрасным подробным объяснениям, приведенным выше, и самым простым способом, который я могу себе представить, это связано с ограничениями встроенного монитора JVM, который 1) приобретается на всем блоке синхронизации (блоке или объекте) и 2) не дискриминирует конкретное условие, которое ожидает / уведомляет / о.

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...