Я знаю, что notify() - пробуждает любой один поток, а notifyAll() - пробуждает все и даёт доступ одному. Но в чём отличие? в одних случаях работает notify(), а в некоторых нет, но с notifyAll() отлично. Как понять их?
There are two notification methods in the condition queue API —
notify and notifyAll. To call either, you must hold the lock
associated with the condition queue object. Calling notify causes
the JVM to select one thread waiting on that condition queue to wake
up; calling notifyAll wakes up all the threads waiting on that
condition queue. Because you must hold the lock on the condition queue
object when calling notify or notifyAll, and waiting threads
cannot return from wait without reacquiring the lock, the notifying
thread should release the lock quickly to ensure that the waiting
threads are unblocked as soon as possible.
Because multiple threads
could be waiting on the same condition queue for different condition
predicates, using notify instead of notifyAll can be dangerous,
primarily because single notification is prone to a problem akin to
missed signals.
© Brian Goetz "Java Concurrency in Practice"
Использование notify потенциально более бережливо к ресурсам - не приходиться выводить потоки из спячки, не приходиться парковать их обратно, потокам не приходиться драться за блокировку и процессор в момент пробуждения. Но в некоторых ситуациях пробуждение только одного потока может приводить к потере сигнала и зависанию программы.
notify() - нотифает только 1 поток, notifyAll() - нотифает все потоки.
Может вы пробуждаете не тот поток, который вам нужен, а notifyAll() пробуждает сразу все потоки.
Из вопроса на enSO:
Примером может служить набор потоков, ожидающих завершения
определенной задачи; как только задача будет завершена, все ожидающие
потоки могут продолжать свою деятельность. В таком случае вы будете
использовать notifyAll() для одновременного пробуждения всех
ожидающих потоков.
Другой случай, например взаимоисключающая блокировка, только один из
ожидающих потоков может сделать что-то полезное после уведомления (в
этом случае получить блокировку). В таком случае вы предпочитаете
использовать notify().
Полезные различия:
Используйте notify(), если все ваши ожидающие потоки взаимозаменяемы
(порядок, который они просыпают, не имеет значения), или если у вас
только один ожидающий поток. Общим примером является пул потоков,
используемый для выполнения заданий из очереди - при добавлении
задания один из потоков уведомляется о пробуждении, выполняет
следующее задание и возвращается в режим сна.
Используйте notifyAll() для других случаев, когда потоки ожидания
могут иметь разные цели и должны иметь возможность запускать
одновременно. Примером может служить операция обслуживания совместно
используемого ресурса, где несколько потоков ждут завершения операции
перед доступом к ресурсу.
Если коротко и без занудства - notify будит один какой то рандомный поток, notifyAll - (внезапно
Сборка персонального компьютера от Artline: умный выбор для современных пользователей