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

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

Открыта

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

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

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

Если коллекция пустая, то ждем
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 никуда.

Комментарии (57)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Marat Safiianov
Уровень 38
3 июля 2025, 12:40
if проверяется только один раз, а while — проверяется каждый раз после пробуждения. 🔁 Как это работает: ✅ while:
while (jobs.size() == 0) {
    wait(); // Поток спит
}
// после пробуждения снова проверим jobs.size() == 0 Ты проснулся → СНОВА проверяешь условие. Если очередь всё ещё пуста — снова спишь. Если уже нет — продолжаешь работу. ⚠️ if:
if (jobs.size() == 0) {
    wait(); // Поток спит
}
// проснулся — НЕ проверяешь условие заново Ты проснулся — сразу идёшь работать. Даже если очередь снова пуста → 💥 ошибка.
Anonymous #311541 Java Developer
12 мая 2024, 12:43
"Зачем это надо. Дело в том, что если нить разбудили – это еще не значит, что условие выполнится. Может, там таких спящих нитей было два десятка. Разбудили всех, а задание забрать сможет только одна." Неожиданный вывод: "Грубо говоря, могут быть «ложные побудки». Хороший разработчик должен учитывать это дело." Помоему очень нелогично составлены предложения. Но инфа полезная и правильная. читаем доки 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 QA Automation Engineer
29 февраля 2024, 17:48
Объясните, пожалуйста, кто понял разницу между if и while. Я не понимаю в чем разница то, нить проснется после нотифая и пойдет дальше выполнять код что в первом случае, что во втором
Anonymous #3369914
Уровень 42
Expert
1 марта 2024, 14:10
Получается, что после просыпания в блоке while еще раз будет проверенно условие. В случае if просто пойдет дальше.
vonger QA Automation Engineer
1 марта 2024, 16:58
ну это странно, почему в случае с if так не работает, по идее с while проверка происходит на входе, а не в теле цикла, так же как и с if, поэтому в обоих случаях просто должна продолжится работа, а не проверятся условие
Андрей
Уровень 51
13 марта 2024, 08:22
while в конце каждой итерации проверяет условие: а нужно ли запускать код еще раз?
Нейросеть
Уровень 41
19 мая 2024, 13:26
Предположим 100 нитей заснули в теле if и после notifyAll проснутся все. 100 нитей вызовут метод jobs.remove(0), а там может быть всего одну задачу добавили. Соответственно обратно все кроме одной вернут в лучшем случае null и где нибудь в тридесятом вызове программа выкинет NullPointerException, а в худшем сразу упадёт когда 99 нитей попытаются получить и удалить то чего нет
Kirill
Уровень 46
12 ноября 2024, 13:38
Не вернут они null. Возникнет IndexOutOfBoundsException при попытке извлечения элемента с индексом 0 из пустого списка.
20 августа, 13:00
Похоже, после пробуждения, нить не начинает исполнять метод с самого начала, а продолжает с того места, где уснула. Собственно, уснув, она исполнил метод wait, проснувшись, продолжит исполнение со следующей за wait строки. Если там бы был if, то исполнение кода вышло бы из if и продолжилось бы дальше. Цикл же продолжится следующей итерацией.
wokku
Уровень 51
20 августа 2023, 15:59
И вот, будучи студентом 43 уровня, я столкнулся с нехваткой материи для продолжения)
Lo
Уровень 35
Expert
30 декабря 2023, 15:40
А сейчас как, хватает?
Fl1s Backend Developer
12 августа 2023, 04:28
return jobs.remove(0);
Что делает эта строчка?
Grock
Уровень 44
17 сентября 2023, 20:11
Возвращает элемент под индексом 0 после его удаления из коллекции.
Fl1s Backend Developer
18 сентября 2023, 07:13
Спс
Loony.java
Уровень 46
4 мая 2023, 14:59
Очень помогла вот эта статья, особенно гифки) Мб кому-то тоже поможет визуализировать всё в голове и так проще запомнится: Хабр
Afanasii
Уровень 51
2 июля 2023, 20:15
гифки классные ага :)
Rolik
Уровень 41
24 апреля 2023, 08:40
Такой бардак всего из за двух типов: long и double (64 бит). Т.е., либо пишем все в интах, либо все атомарим и sleepим.
Kirill
Уровень 46
12 ноября 2024, 13:23
И много ты в последнее время встречал 32 разрядных машин? Просто на 64 разрядных чтение/запись типов long и double тоже происходять атомарно.
LuneFoxJava Developer в BIFITExpert
8 октября 2021, 17:45
Спасибо, 2 минуты играл в "найди отличия" :) Как становиться программистом с такой слепотой куриной-то, а?
Уpовень 302
25 ноября 2021, 21:12
в смысле там есть отличия?
LuneFoxJava Developer в BIFITExpert
26 ноября 2021, 00:09
if и while :)
Уpовень 302
26 ноября 2021, 10:44
и правда...
12 апреля 2024, 18:39
НЕ ПОНЯЛ, 302 УРОВЕНЬ?)
21 сентября 2021, 16:37
Какая каша в голове . когда уже в моей голове появиться шкаф с полками .
SERGEY
Уровень 31
17 февраля 2022, 16:24
вызывай метод wait в цикле, потому, что нить может проснуться и попытаться удалить элемент из пустого списка. В последствии вызвав exception. Если и до тех пор - разница ощутима как теплый и мягкий
Максим
Уровень 34
26 февраля 2021, 17:16