Ответ
Методы wait(), notify() и notifyAll() определены в классе Object и используются для межпоточной синхронизации (cooperative threading). Они должны вызываться внутри синхронизированного блока или метода.
Принцип работы:
- Поток захватывает монитор объекта (через
synchronized). - Вызов
wait()освобождает монитор и переводит текущий поток в состояние ожидания. - Другой поток, захватив тот же монитор, вызывает
notify()(разбудить один случайный поток) илиnotifyAll()(разбудить все потоки). - Пробужденный поток пытается перезахватить монитор и продолжает выполнение после вызова
wait().
Пример шаблона "Producer-Consumer":
class SharedBuffer {
private final Queue<Integer> queue = new LinkedList<>();
private final int capacity;
public SharedBuffer(int capacity) { this.capacity = capacity; }
// Метод производителя
public synchronized void produce(int value) throws InterruptedException {
while (queue.size() == capacity) { // Всегда используйте цикл для проверки условия!
wait(); // Ждет, пока потребитель не освободит место
}
queue.add(value);
System.out.println("Produced: " + value);
notifyAll(); // Уведомляет ожидающих потребителей (и производителей)
}
// Метод потребителя
public synchronized int consume() throws InterruptedException {
while (queue.isEmpty()) { // Проверка в цикле на spurious wakeup
wait(); // Ждет, пока производитель не добавит данные
}
int value = queue.poll();
System.out.println("Consumed: " + value);
notifyAll(); // Уведомляет ожидающих производителей
return value;
}
}
Ключевые правила:
- Всегда вызывайте эти методы внутри
synchronizedблока, владея монитором того же объекта. - Всегда проверяйте условие ожидания в цикле
while, а не вif. Это защищает от ложных пробуждений (spurious wakeups). - Предпочитайте
notifyAll()для простоты, если нет строгих требований к производительности. - В современном коде часто лучше использовать высокоуровневые средства из
java.util.concurrent(например,BlockingQueue,LocksсCondition).