Есть реализация 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/
Виртуальный выделенный сервер (VDS) становится отличным выбором
Есть приложение, которое воспроизводит онлайн радио
Есть массив byte[]Чем ограничивается его максимальный размер? Влияет ли на это размер стека Xss для JVM?
как сделать replace all, что бы осталось [{gvdfdf}, {vxcbnn}]
Нужно удалить(сделать неактивными) ссылки в WebView на AndroidСтраницу WebView получаем через: