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