wait, notify и notifyAll
Эти методы принадлежат Object и работают с wait set конкретного монитора.
Контракт
- Поток обязан владеть тем же монитором, иначе
IllegalMonitorStateException. wait()атомарно помещает поток в ожидание и освобождает этот монитор.- После сигнала поток не продолжает сразу: он снова конкурирует за монитор.
- Условие проверяют в
while, потому что возможны ложные пробуждения и другой поток может изменить состояние до повторного захвата.
final class OneSlot<T> {
private T value;
private boolean full;
public synchronized void put(T next) throws InterruptedException {
while (full) {
wait();
}
value = next;
full = true;
notifyAll();
}
public synchronized T take() throws InterruptedException {
while (!full) {
wait();
}
T result = value;
value = null;
full = false;
notifyAll();
return result;
}
}notify() будит один произвольно выбранный поток. Если на одном мониторе ждут разные условия, можно разбудить неподходящего и получить зависание. notifyAll() будит всех, после чего каждый перепроверяет свой predicate; это надежнее, хотя создает лишнюю конкуренцию.
wait() освобождает только монитор, на котором вызван. Другие мониторы и Lock остаются захваченными. Interrupted waiting завершится InterruptedException.
На практике готовая BlockingQueue, CountDownLatch или Condition чаще выражает протокол точнее.
На собеседовании
- Почему
wait()только внутри synchronized? Нужно атомарно проверить условие и заснуть без потерянного сигнала. - Почему
while, а неif? Ложные пробуждения и конкуренция после сигнала. - Продолжает ли поток сразу после
notify()? Нет, сначала снова захватывает монитор. - Освобождает ли
wait()все блокировки? Нет, только монитор вызова. - Чем
notifyAll()безопаснее? Все ожидающие перепроверяют свои условия.