Важное различие между заблокированным состоянием и состоянием ожидания - влияние на планировщик. Поток в заблокированном состоянии борется за блокировку; этот поток по-прежнему считается чем-то, что планировщик должен обслуживать, и, возможно, учитывается в решениях планировщика о том, сколько времени дать запущенным потокам (чтобы он мог дать шанс потокам, блокирующим блокировку).
Когда поток находится в состоянии ожидания, нагрузка, которую он оказывает на систему, сводится к минимуму, и планировщику не нужно об этом беспокоиться. Он бездействует, пока не получит уведомление. За исключением того факта, что он сохраняет занятость потока ОС, он полностью не работает.
Вот почему использование notifyAll далеко не идеально, оно приводит к тому, что куча потоков, которые ранее благополучно бездействовали, не загружая систему, просыпается, где большинство из них будут блокироваться, пока не получат блокировку, найдут состояние, в котором они находятся. ожидание не соответствует действительности, и возвращайтесь к ожиданию. Было бы предпочтительнее уведомлять только те потоки, у которых есть шанс добиться прогресса.
(Использование ReentrantLock вместо внутренних блокировок позволяет вам иметь несколько условий для одной блокировки, так что вы можете убедиться, что уведомляемый поток - это тот, который ожидает определенного условия, избегая ошибки с потерянным уведомлением в случае, если поток получает уведомление для то, на что он не может воздействовать.)