JavaRush /Курсы /Java Multithreading /Нюансы работы

Нюансы работы

Java Multithreading
7 уровень , 8 лекция
Открыта

— Привет, Амиго!

И еще пара деталей. Так сказать практических советов.

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

Если коллекция пустая, то ждем
public synchronized Runnable getJob()
{
 if (jobs.size()==0)
  this.wait();

 return jobs.remove(0);
}

В документации по Java очень старательно советуют вызвать метод wait в цикле:

Если коллекция пустая, то ждем
public synchronized Runnable getJob()
{
 while (jobs.size()==0)
  this.wait();

 return jobs.remove(0);
}

Зачем это надо. Дело в том, что если нить разбудили – это еще не значит, что условие выполнится. Может, там таких спящих нитей было два десятка. Разбудили всех, а задание забрать сможет только одна.

Грубо говоря, могут быть «ложные побудки». Хороший разработчик должен учитывать это дело.

— Ясно. А не проще ли тогда использовать просто notify?

— А если в списке больше чем одно задание? Notify обычно советуют использовать ради оптимизации. Во всех остальных случаях рекомендуют использовать метод notifyAll.

— Ок.

— Но и это еще не все. Во-первых, может возникнуть ситуация, когда кто-то унаследовался от твоего класса, добавил туда свои методы и тоже использует wait/notifyAll. Т.е. может быть ситуация, когда на одном объекте висят независимые пары wait/notifyAll, которые друг о друге не знают. Поэтому что надо делать?

— Всегда вызывать wait в цикле и проверять, что условие выхода из цикла действительно выполнилось!

— Правильно. А чтобы тебе стало совсем понятно, что от этого никуда не деться, то многие разработчики указывают на то, что иногда нити просыпаются сами. Нити, которые гарантированно никто не может будить случайно. Похоже это побочный процесс оптимизации/ускорения кода в работающей Java-машине.

— Ничего себе. Понял, без цикла перед wait никуда.

Комментарии (56)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Marat Safiianov Уровень 41
3 июля 2025
if проверяется только один раз, а while — проверяется каждый раз после пробуждения. 🔁 Как это работает: ✅ while:

while (jobs.size() == 0) {
    wait(); // Поток спит
}
// после пробуждения снова проверим jobs.size() == 0 Ты проснулся → СНОВА проверяешь условие. Если очередь всё ещё пуста — снова спишь. Если уже нет — продолжаешь работу. ⚠️ if:

if (jobs.size() == 0) {
    wait(); // Поток спит
}
// проснулся — НЕ проверяешь условие заново Ты проснулся — сразу идёшь работать. Даже если очередь снова пуста → 💥 ошибка.
Anonymous #311541 Уровень 1
12 мая 2024
"Зачем это надо. Дело в том, что если нить разбудили – это еще не значит, что условие выполнится. Может, там таких спящих нитей было два десятка. Разбудили всех, а задание забрать сможет только одна." Неожиданный вывод: "Грубо говоря, могут быть «ложные побудки». Хороший разработчик должен учитывать это дело." Помоему очень нелогично составлены предложения. Но инфа полезная и правильная. читаем доки wait(long timeoutMillis, int nanos): A thread can 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. See the example below. API Note: The recommended approach to waiting is to check the condition being awaited in a while loop around the call to wait, as shown in the example below. Among other things, this approach avoids problems that can be caused by spurious wakeups.

synchronized (obj) {
    while ( <condition does not hold and timeout not exceeded> ) {
        long timeoutMillis = ... ; // recompute timeout values
        int nanos = ... ;
        obj.wait(timeoutMillis, nanos);
    }
    ... // Perform action appropriate to condition or timeout
}
vonger Уровень 51
29 февраля 2024
Объясните, пожалуйста, кто понял разницу между if и while. Я не понимаю в чем разница то, нить проснется после нотифая и пойдет дальше выполнять код что в первом случае, что во втором
Anonymous #3369914 Уровень 42 Expert
1 марта 2024
Получается, что после просыпания в блоке while еще раз будет проверенно условие. В случае if просто пойдет дальше.
vonger Уровень 51
1 марта 2024
ну это странно, почему в случае с if так не работает, по идее с while проверка происходит на входе, а не в теле цикла, так же как и с if, поэтому в обоих случаях просто должна продолжится работа, а не проверятся условие
Андрей Уровень 51
13 марта 2024
while в конце каждой итерации проверяет условие: а нужно ли запускать код еще раз?
Нейросеть Уровень 41
19 мая 2024
Предположим 100 нитей заснули в теле if и после notifyAll проснутся все. 100 нитей вызовут метод jobs.remove(0), а там может быть всего одну задачу добавили. Соответственно обратно все кроме одной вернут в лучшем случае null и где нибудь в тридесятом вызове программа выкинет NullPointerException, а в худшем сразу упадёт когда 99 нитей попытаются получить и удалить то чего нет
Kirill Уровень 46
12 ноября 2024
Не вернут они null. Возникнет IndexOutOfBoundsException при попытке извлечения элемента с индексом 0 из пустого списка.
wokku Уровень 51
20 августа 2023
И вот, будучи студентом 43 уровня, я столкнулся с нехваткой материи для продолжения)
Lo Уровень 35 Expert
30 декабря 2023
А сейчас как, хватает?
Fl1s Уровень 51
12 августа 2023

return jobs.remove(0);
Что делает эта строчка?
Grock Уровень 44
17 сентября 2023
Возвращает элемент под индексом 0 после его удаления из коллекции.
Fl1s Уровень 51
18 сентября 2023
Спс
Loony.java Уровень 46
4 мая 2023
Очень помогла вот эта статья, особенно гифки) Мб кому-то тоже поможет визуализировать всё в голове и так проще запомнится: Хабр
Afanasii Уровень 51
2 июля 2023
гифки классные ага :)
Rolik Уровень 41
24 апреля 2023
Такой бардак всего из за двух типов: long и double (64 бит). Т.е., либо пишем все в интах, либо все атомарим и sleepим.
Kirill Уровень 46
12 ноября 2024
И много ты в последнее время встречал 32 разрядных машин? Просто на 64 разрядных чтение/запись типов long и double тоже происходять атомарно.
LuneFox Уровень 41 Expert
8 октября 2021
Спасибо, 2 минуты играл в "найди отличия" :) Как становиться программистом с такой слепотой куриной-то, а?
Уровень 51
25 ноября 2021
в смысле там есть отличия?
LuneFox Уровень 41 Expert
26 ноября 2021
if и while :)
Уровень 51
26 ноября 2021
и правда...
12 апреля 2024
НЕ ПОНЯЛ, 302 УРОВЕНЬ?)
21 сентября 2021
Какая каша в голове . когда уже в моей голове появиться шкаф с полками .
SERGEY Уровень 31
17 февраля 2022
вызывай метод wait в цикле, потому, что нить может проснуться и попытаться удалить элемент из пустого списка. В последствии вызвав exception. Если и до тех пор - разница ощутима как теплый и мягкий
Максим Уровень 34
26 февраля 2021