Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое notify?
notify() — это метод в Java для синхронизации потоков. Это один из самых старых (и сложных) механизмов многопоточности, основанный на Monitor pattern.
Основы: wait() и notify()
В Java есть три связанных метода, определённых в Object класса:
public final void wait() // Ждать, пока другой поток вызовет notify
public final void notify() // Разбудить один ожидающий поток
public final void notifyAll() // Разбудить все ожидающие потоки
Ключевое правило: эти методы ВСЕГДА должны быть вызваны внутри synchronized блока:
synchronized(lock) {
while (!condition) {
lock.wait(); // Отпускаем lock и ждем
}
// Выполняем работу
}
// В другом потоке:
synchronized(lock) {
// Меняем состояние
condition = true;
lock.notify(); // Разбуждаем поток
}
Как работает notify()?
Когда поток вызывает wait():
- Освобождает монитор (synchronized lock)
- Засыпает (переходит в WAITING состояние)
- Другой поток может захватить тот же монитор
- Когда другой поток вызывает notify(), ждущий поток просыпается
- Но он НЕ тут же выполняется! Ему сначала нужно заново захватить монитор
Поток 1 Поток 2
───────────────────────────── ─────────────────────────────
synchronized(lock) { synchronized(lock) {
wait() condition = true
↓ notify() ← просыпаешься
(WAITING, без lock) }
↓
просыпается (требует lock) lock свободен
захватывает lock
продолжает выполнение
}
Практический пример: Producer-Consumer
public class ProducerConsumer {
private int buffer = 0;
private boolean empty = true;
private final Object lock = new Object();
// Producer: производит данные
public void produce(int value) throws InterruptedException {
synchronized(lock) {
while (!empty) {
// Буфер полон, жди пока Consumer заберет
lock.wait();
}
buffer = value;
empty = false;
System.out.println("Produced: " + value);
// Разбуди Consumer
lock.notify();
}
}
// Consumer: потребляет данные
public int consume() throws InterruptedException {
synchronized(lock) {
while (empty) {
// Буфер пустой, жди Producer
lock.wait();
}
int value = buffer;
empty = true;
System.out.println("Consumed: " + value);
// Разбуди Producer
lock.notify();
return value;
}
}
}
Использование:
ProducerConsumer pc = new ProducerConsumer();
Thread producer = new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
pc.produce(i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread consumer = new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
pc.consume();
Thread.sleep(200);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
producer.start();
consumer.start();
Вывод:
Produced: 1
Consumed: 1
Produced: 2
Consumed: 2
Produced: 3
Consumed: 3
// ...
notify() vs notifyAll()
notify() — разбуждает ОДИН ждущий поток (непредсказуемо какой):
Object lock = new Object();
// Несколько потоков ждут
Thread t1 = new Thread(() -> {
synchronized(lock) {
lock.wait(); // WAITING
System.out.println("T1 пробудился");
}
});
Thread t2 = new Thread(() -> {
synchronized(lock) {
lock.wait(); // WAITING
System.out.println("T2 пробудился");
}
});
Thread t3 = new Thread(() -> {
synchronized(lock) {
lock.notify(); // Разбудит ТОЛЬКО одного (T1 или T2)
System.out.println("T3 разбудил одного");
}
});
t1.start();
t2.start();
Thread.sleep(100);
t3.start();
notifyAll() — разбуждает ВСЕ ждущие потоки:
Thread t3 = new Thread(() -> {
synchronized(lock) {
lock.notifyAll(); // Разбудит и T1, и T2
System.out.println("T3 разбудил всех");
}
});
Важное правило: используй while, не if
// НЕПРАВИЛЬНО - может привести к spurious wakeup
synchronized(lock) {
if (!condition) {
lock.wait();
}
// Может произойти spurious wakeup (пробуждение без notify)
// и condition может быть false!
}
// ПРАВИЛЬНО - проверяем условие после пробуждения
synchronized(lock) {
while (!condition) {
lock.wait();
}
// Гарантированно condition == true
}
Пример с несколькими потребителями
public class ThreadSafeQueue {
private Queue<Integer> queue = new LinkedList<>();
private static final int MAX_SIZE = 5;
private final Object lock = new Object();
public void produce(int value) throws InterruptedException {
synchronized(lock) {
while (queue.size() >= MAX_SIZE) {
lock.wait(); // Жди, пока есть место
}
queue.add(value);
System.out.println("Produced: " + value);
lock.notifyAll(); // Разбуди всех
}
}
public int consume() throws InterruptedException {
synchronized(lock) {
while (queue.isEmpty()) {
lock.wait(); // Жди данные
}
int value = queue.poll();
System.out.println("Consumed: " + value);
lock.notifyAll(); // Разбуди всех
return value;
}
}
}
Проблемы с notify()
1. Spurious wakeup
Поток может пробудиться БЕЗ вызова notify():
synchronized(lock) {
while (!condition) { // ВСЕГДА используй while!
lock.wait(); // Может пробудиться спонтанно
}
}
2. Lost wakeup
Если notify() вызван раньше wait(), сигнал потеряется:
Thread t1 = new Thread(() -> {
synchronized(lock) {
Thread.sleep(100); // Задержка
lock.wait(); // Поток t2 уже вызвал notify()!
System.out.println("Никогда не будет выполнено"); // DEADLOCK
}
});
Thread t2 = new Thread(() -> {
synchronized(lock) {
lock.notify(); // Разбуждает никого
}
});
t2.start();
t1.start();
Современные альтернативы
В Java 5+ есть лучшие способы синхронизации:
// Вместо notify - используй Condition
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
try {
while (!condition) {
condition.await(); // Более удобно чем wait()
}
condition.signalAll(); // Более удобно чем notifyAll()
} finally {
lock.unlock();
}
// Или используй BlockingQueue
BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(5);
queue.put(value); // Блокируется если полная
int value = queue.take(); // Блокируется если пустая
Когда использовать notify()
- Очень редко в современном коде
- Только для старого кода или очень специфичных случаев
- Вместо этого используй:
- BlockingQueue
- Condition variables
- CountDownLatch
- CyclicBarrier
- Semaphore
Заключение
notify():
- Пробуждает один ждущий поток
- Требует synchronized
- Используется с wait() для синхронизации
- ВАЖНО: всегда используй while для проверки условия
- ВАЖНО: используй notifyAll() вместо notify() обычно
- В современном коде предпочитай Condition или BlockingQueue
- Очень легко сделать ошибку (deadlock, spurious wakeup, lost wakeup)