Что произойдет, если мы вызовем condition.await() несколько раз текущим потоком в Java

Я хотел бы понять, что на самом деле происходит, когда мы выполняем следующий фрагмент кода.

Когда поток выполняет someMethod() и попадает в цикл while, что произойдет, если он снова и снова будет вызывать метод await? Как контекст потока хранится/поддерживается блокировкой Condition/ReentrantLock?

Кроме того, когда поток вызывает await(), это означает, что он освобождает текущую блокировку и переходит в состояние ожидания. Означает ли это, что другой поток может получить блокировку? Если да, то какой смысл писать цикл while вокруг метода await()?

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
...
...
    public void someMethod() {
        lock.lock();
        try {
            for (int i = 2; i < n; i = i + 2) {
                while (state != 2) {
                    condition.await();
                }
                printNumber.accept(i);
                r.run();
                state = 0;
                condition.signalAll();
            }
        } finally {
            lock.unlock();
        }
    }

Пожалуйста, поправьте меня, если я неправильно понимаю. Заранее спасибо.


person Prasanthi B    schedule 05.03.2021    source источник


Ответы (1)


Смысл этого цикла ожидания, по-видимому, состоит в том, чтобы дождаться, пока состояние не станет равным 2. Пробуждение от вызова ожидания просто означает, что вы проснулись. Это не значит, что состояние равно 2.

Логика такая же, как когда я жду посреди ночи, смотрю на часы и решаю снова лечь спать. :-)

Обратите внимание на документацию по Условие явно указывает, что могут происходить ложные пробуждения. Это означает, что любое использование Condition.await, которое не проверяет фактическое желаемое состояние, почти наверняка содержит ошибку.

Между тем этот код кажется ущербным. Вы знаете, что не собираетесь выполнять вызов signalAll, пока поток ожидает в await, верно? Поскольку поток ожидает в await. Должен быть какой-то другой поток, выполняющий другой код, который может сигнализировать об условии, чтобы вывести этот поток из ожидания.


Ваш вопрос:

Я хотел бы понять, что на самом деле происходит, когда мы выполняем следующий фрагмент кода.

С показанным кодом поток остается в вызове await навсегда, потому что не показан код, который может разбудить его.

(Согласно документации, существует вероятность ложного пробуждения, и если это произойдет, цикл while снова поместит поток в ожидание, потому что ожидаемое не изменится.)

Что касается «вызова ожидания снова и снова» — ну, вы не можете вызывать ожидание, когда ждете, — потому что вам негде что-либо вызывать, пока вы не работаете. Таким образом, нет никакой разницы между первым и последующими вызовами. в каждом случае вызывающий поток переходит в спящий режим до тех пор, пока впоследствии не будет сообщено об условии.

Что касается блокировки - блокировка снимается во время ожидания и восстанавливается при пробуждении. Это практическая необходимость; предположительно в вашем примере блокировка защищает доступ к «состоянию», и любой код, который хочет изменить состояние и разбудить официанта, лучше сделать это под защитой блокировки.

person user15187356    schedule 05.03.2021
comment
Спасибо за подробное объяснение. Буду признателен, если вы также поможете мне понять, как поддерживаются потоки в состоянии ожидания и различаются между ReentrantLock и Condition? - person Prasanthi B; 05.03.2021
comment
Как это сделать, зависит от конкретной реализации Java, но в общих чертах ОС обычно существует очередь исполняемых потоков, и каждый ожидаемый ресурс будет иметь очередь ожидающих потоков. Для части условий версий ReentrantLock я предлагаю написать еще один вопрос. - person user15187356; 05.03.2021