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

Є ще кілька деталей. А точніше практичних порад, про які тобі варто знати.

Припустимо, у тебе є метод, який очікує на щось та засинає, поки умову не виконано.

Якщо колекція порожня, очікуємо
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 тепер нікуди.