Есть реализация Lock механизма на wait и notify:
class Lock {
private boolean isLocked = false;
void lock() throws InterruptedException {
synchronized (this) {
while (isLocked) {
wait();
}
isLocked = true;
}
}
void unlock() {
synchronized (this) {
isLocked = false;
notify();
}
}
}
Обычно в методе lock я вижу цикл while, но мне стало интересно почему так и я решил попробовать изменить на if и получилось так:
void lock() throws InterruptedException {
synchronized (this) {
if (isLocked) {
wait();
}
isLocked = true;
}
}
И получил очень-очень маленькую утечку данных при тестировании на счетчике из 2 потоков вместо 2000000 получается 1999995 плюс минус погрешность...
Мне интересно почему так получается что это за сценарий с таким мизерными шансами на выполнение?
Смысл использования while вместо if в том, что поток может пробудиться из wait, даже если не было вызвано нигде notify.
Этот момент описан в java документации к методу wait:
A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops, like this one:
synchronized (obj) {
while (<condition does not hold>) {
obj.wait();
}
//...
}
Ответ есть на англоязычном StackOverflow
Основная причина, по которой мы используем while, это гонка данных между потоками. Например:
synchronized (queue) {
while (queue.isEmpty ()) {
queue.wait ();
}
queue.remove ();
}
В приведенном выше коде может быть два потока читателя, которые читают из очереди и один писатель, который добавляет элементы в очередь.
Когда писатель блокирует очередь, чтобы добавить к ней элемент, читатель №1 может находиться в «синхронизированной» блокировке, при которой можно прочитать элемент (remove), в то время, когда читатель №2 уже ждет.
Когда элемент добавляется в «очередь», вызывается сообщение notify, тогда читатель №2 уведомляется и перемещается в очередь выполнения, но он находится за читателем №1, ожидающим блокировки queue.
Поскольку читатель №1 первым получает доступ к очереди, удаляя из неё элемент, возникает исключение, если цикл while будет простым if, потому что №2 будет пытаться прочитать элемент, который уже прочитан первым читателем. Поэтому второму читателю нужно ещё раз убедиться, что очередь не пуста, перед тем, как вызывать remove из-за состояния гонки.
Более подробно описано пользователем Gray
Http://256stuff.com/gray/docs/misc/producer_consumer_race_conditions/
Как развивать веб-проекты в 2026 году: технологии, контент E-E-A-T и факторы доверия
Современные инструменты для криптотрейдинга: как технологии помогают принимать решения
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники