Проблема утечки данных в Lock механизме

291
27 апреля 2017, 13:57

Есть реализация 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 плюс минус погрешность... Мне интересно почему так получается что это за сценарий с таким мизерными шансами на выполнение?

Answer 1

Смысл использования 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();
    }
    //...
}
Answer 2

Ответ есть на англоязычном 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/

READ ALSO
есть ли максимальный размер у массивов в java?

есть ли максимальный размер у массивов в java?

Есть массив byte[]Чем ограничивается его максимальный размер? Влияет ли на это размер стека Xss для JVM?

239
Как в данном случае лучше использовать replaceAll?

Как в данном случае лучше использовать replaceAll?

как сделать replace all, что бы осталось [{gvdfdf}, {vxcbnn}]

217
Удаление гиперссылок в WebView(Android)

Удаление гиперссылок в WebView(Android)

Нужно удалить(сделать неактивными) ссылки в WebView на AndroidСтраницу WebView получаем через:

191