Страницы

Поиск по вопросам

пятница, 31 января 2020 г.

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

#java #многопоточность


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


Ответы

Ответ 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 () { obj.wait(); } //... }

Ответ 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/

Комментариев нет:

Отправить комментарий