Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое notifyAll
notifyAll() — это метод класса Object в Java, который пробуждает все потоки, ожидающие монитора объекта. Он используется для синхронизации между потоками.
Основные концепции
Monitor (Монитор) объекта
Каждый объект в Java имеет монитор — механизм для контроля доступа нескольких потоков:
public class SharedResource {
private int value = 0;
// Блокирует доступ к методу для других потоков
public synchronized void setValue(int newValue) {
this.value = newValue;
notifyAll(); // Пробуждает все ждущие потоки
}
public synchronized int getValue() {
while (value == 0) {
try {
wait(); // Текущий поток ждёт
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
return value;
}
}
Методы синхронизации
wait() — ждать
// Потокбезопасный метод для ожидания
public synchronized void waitForValue() throws InterruptedException {
while (value == 0) {
wait(); // Текущий поток ждёт, пока другой поток вызовет notify()
}
}
notify() vs notifyAll()
// notify() — пробуждает ОДИН случайный поток
public synchronized void notifyOne() {
notify(); // Просыпается только один поток
}
// notifyAll() — пробуждает ВСЕ ждущие потоки
public synchronized void notifyAll() {
notifyAll(); // Просыпаются все потоки
}
Практический пример: Producer-Consumer Pattern
public class Buffer {
private Queue<String> queue = new LinkedList<>();
private final int maxSize = 10;
// Производитель добавляет элементы
public synchronized void produce(String item) throws InterruptedException {
while (queue.size() == maxSize) {
wait(); // Ждём, если буфер полный
}
queue.add(item);
System.out.println("Добавлено: " + item);
notifyAll(); // Пробуждаем потребителей
}
// Потребитель забирает элементы
public synchronized String consume() throws InterruptedException {
while (queue.isEmpty()) {
wait(); // Ждём, если буфер пуст
}
String item = queue.poll();
System.out.println("Извлечено: " + item);
notifyAll(); // Пробуждаем производителей
return item;
}
}
// Использование
public class Main {
public static void main(String[] args) {
Buffer buffer = new Buffer();
// Производитель
new Thread(() -> {
try {
for (int i = 0; i < 20; i++) {
buffer.produce("Item " + i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
// Потребитель
new Thread(() -> {
try {
for (int i = 0; i < 20; i++) {
buffer.consume();
Thread.sleep(200);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
Процесс работы notifyAll()
// Шаг 1: Потоки ждут
Thread t1 = new Thread(() -> {
synchronized(lock) {
try {
lock.wait(); // t1 ждёт, отпускает монитор
} catch (InterruptedException e) {}
}
});
Thread t2 = new Thread(() -> {
synchronized(lock) {
try {
lock.wait(); // t2 ждёт, отпускает монитор
} catch (InterruptedException e) {}
}
});
// Шаг 2: Главный поток вызывает notifyAll()
Thread main = new Thread(() -> {
synchronized(lock) {
lock.notifyAll(); // Пробуждает t1 и t2
// t1 и t2 остаются в blocked state до конца этого блока
}
// Теперь t1 и t2 могут получить монитор
});
Когда использовать notifyAll() вместо notify()
1. Множество потоков с разными условиями
public class Warehouse {
private List<String> items = new ArrayList<>();
// Разные потребители ждут разных товаров
public synchronized String getApple() throws InterruptedException {
while (!items.contains("apple")) {
wait();
}
items.remove("apple");
notifyAll(); // Уведомляем всех, может потребоваться другому потребителю
return "apple";
}
public synchronized String getOrange() throws InterruptedException {
while (!items.contains("orange")) {
wait();
}
items.remove("orange");
notifyAll(); // Уведомляем всех
return "orange";
}
public synchronized void add(String item) {
items.add(item);
notifyAll(); // notifyAll() безопаснее notify()
}
}
2. Сложные условия ожидания
public class BankAccount {
private int balance = 0;
public synchronized void withdraw(int amount) throws InterruptedException {
// Может быть несколько потоков с разными суммами
while (balance < amount) {
wait(); // Ждём больше денег
}
balance -= amount;
notifyAll(); // Может помочь и тем, кто делает deposit
}
public synchronized void deposit(int amount) {
balance += amount;
notifyAll(); // Пробуждаем всех потребителей
}
}
wait() с timeout
public synchronized String getWithTimeout(long timeoutMs)
throws InterruptedException {
long deadline = System.currentTimeMillis() + timeoutMs;
while (queue.isEmpty()) {
long remaining = deadline - System.currentTimeMillis();
if (remaining <= 0) {
return null; // Timeout
}
wait(remaining); // Ждём с таймаутом
}
return queue.poll();
}
notify() vs notifyAll() — таблица сравнения
| Аспект | notify() | notifyAll() |
|---|---|---|
| Количество пробуждённых потоков | 1 (случайный) | Все |
| Производительность | Лучше | Чуть хуже |
| Безопасность | Опасен (может случиться deadlock) | Безопаснее |
| Условие пробуждения | ОДНО | МНОЖЕСТВО |
| Spurious wakeup protection | Нужна | Нужна |
Spurious wakeup — ложное пробуждение
// НЕПРАВИЛЬНО — может привести к ошибкам
if (condition) {
try {
wait();
} catch (InterruptedException e) {}
}
process(); // Может выполниться при ложном пробуждении!
// ПРАВИЛЬНО — всегда используй while
while (!condition) {
try {
wait();
} catch (InterruptedException e) {}
}
process(); // Безопасно
Современная альтернатива: Locks и Conditions
import java.util.concurrent.locks.*;
public class ModernBuffer {
private final Lock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
private Queue<String> queue = new LinkedList<>();
private final int maxSize = 10;
public void produce(String item) throws InterruptedException {
lock.lock();
try {
while (queue.size() == maxSize) {
notFull.await(); // Более гибко, чем wait()
}
queue.add(item);
notEmpty.signalAll(); // Аналог notifyAll()
} finally {
lock.unlock();
}
}
public String consume() throws InterruptedException {
lock.lock();
try {
while (queue.isEmpty()) {
notEmpty.await();
}
String item = queue.poll();
notFull.signalAll();
return item;
} finally {
lock.unlock();
}
}
}
Лучшие практики
- Всегда используй notifyAll() — это безопаснее, чем notify()
- Используй while для проверки условия, не if
- Ловите InterruptedException — это важно для graceful shutdown
- Рассмотрите Locks и Conditions — они удобнее для сложной логики
- Избегайте wait/notify в новом коде — используй BlockingQueue, CompletableFuture и др.
Заключение
notifyAll() — это метод для пробуждения всех потоков, ожидающих монитора объекта. Он необходим для правильной синхронизации между потоками, но современные альтернативы (Locks, BlockingQueue, CompletableFuture) часто предпочтительнее для новых приложений.