wait и notify
Методы Object.wait(), Object.notify() и Object.notifyAll() —
низкоуровневый механизм координации потоков. Они позволяют потоку
приостановиться внутри синхронизированного блока, пока другой поток не
сообщит, что условие, которого он ждёт, выполнено.
Все три метода объявлены в java.lang.Object и вызываются только когда
поток уже владеет монитором этого объекта — то есть внутри synchronized.
wait() освобождает монитор и переводит поток в состояние WAITING;
notify() будит один из ожидающих потоков; notifyAll() будит все
потоки, ожидающие на этом мониторе.
Синтаксис
synchronized (lock) {
while (!condition) {
lock.wait(); // освобождает монитор и ждёт
}
// условие выполнено
}
// в другом потоке:
synchronized (lock) {
// изменили состояние
lock.notifyAll(); // или notify()
}
Пример 1. Простое ожидание сигнала
public class SimpleWait {
private static final Object LOCK = new Object();
private static boolean ready = false;
public static void main(String[] args) throws InterruptedException {
Thread waiter = new Thread(() -> {
synchronized (LOCK) {
while (!ready) {
try { LOCK.wait(); } catch (InterruptedException ignored) {}
}
System.out.println("получил сигнал");
}
});
waiter.start();
Thread.sleep(300);
synchronized (LOCK) {
ready = true;
LOCK.notify();
System.out.println("отправил сигнал");
}
waiter.join();
}
}
Output:
отправил сигнал
получил сигнал
Пример 2. Producer-Consumer на одном объекте
public class ProducerConsumer {
private static final Object LOCK = new Object();
private static Integer value = null;
public static void main(String[] args) {
Thread producer = new Thread(() -> {
for (int i = 1; i <= 3; i++) {
synchronized (LOCK) {
while (value != null) {
try { LOCK.wait(); } catch (InterruptedException e) { return; }
}
value = i;
System.out.println("produced " + i);
LOCK.notifyAll();
}
}
});
Thread consumer = new Thread(() -> {
for (int i = 1; i <= 3; i++) {
synchronized (LOCK) {
while (value == null) {
try { LOCK.wait(); } catch (InterruptedException e) { return; }
}
System.out.println("consumed " + value);
value = null;
LOCK.notifyAll();
}
}
});
producer.start();
consumer.start();
}
}
Output:
produced 1
consumed 1
produced 2
consumed 2
produced 3
consumed 3
Пример 3. notify vs notifyAll
public class NotifyAllDemo {
private static final Object LOCK = new Object();
private static boolean go = false;
public static void main(String[] args) throws InterruptedException {
Runnable waiter = () -> {
synchronized (LOCK) {
while (!go) {
try { LOCK.wait(); } catch (InterruptedException e) { return; }
}
System.out.println(Thread.currentThread().getName() + " проснулся");
}
};
Thread a = new Thread(waiter, "A");
Thread b = new Thread(waiter, "B");
Thread c = new Thread(waiter, "C");
a.start(); b.start(); c.start();
Thread.sleep(200);
synchronized (LOCK) {
go = true;
LOCK.notifyAll(); // разбудит всех; notify() — только одного
}
a.join(); b.join(); c.join();
}
}
Output (порядок может различаться):
A проснулся
B проснулся
C проснулся
Пример 4. wait с таймаутом
public class WaitTimeout {
private static final Object LOCK = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
synchronized (LOCK) {
long t0 = System.currentTimeMillis();
try { LOCK.wait(300); } catch (InterruptedException ignored) {}
long elapsed = System.currentTimeMillis() - t0;
System.out.println("ждал около " + (elapsed / 100 * 100) + " мс");
}
});
t.start();
t.join();
}
}
Output:
ждал около 300 мс
Пример 5. Ожидание данных датчика
public class SensorBuffer {
private final Object lock = new Object();
private Double last = null;
public void publish(double v) {
synchronized (lock) {
last = v;
lock.notifyAll();
}
}
public double take() throws InterruptedException {
synchronized (lock) {
while (last == null) lock.wait();
double v = last;
last = null;
return v;
}
}
public static void main(String[] args) throws InterruptedException {
SensorBuffer buf = new SensorBuffer();
new Thread(() -> {
try { Thread.sleep(200); buf.publish(42.5); }
catch (InterruptedException ignored) {}
}).start();
System.out.println("получено: " + buf.take());
}
}
Output:
получено: 42.5
Подводные камни
Предупреждение
wait()/notify() бросают IllegalMonitorStateException, если
вызваны вне synchronized-блока на том же объекте.
Предупреждение
Всегда проверяйте условие в цикле while, а не в if — возможны
spurious wakeups (поток просыпается без причины).
Совет
Для большинства практических задач предпочтительнее высокоуровневые
средства из java.util.concurrent: BlockingQueue, Semaphore,
CountDownLatch, Condition. См. Параллельные коллекции.
См. также
Примечание
Материал подготовлен на основе раздела «Concurrency» из Oracle Java Tutorials (docs.oracle.com/javase/tutorial/essential/concurrency/) и распространяется в соответствии с Oracle Free Documentation License. Тексты и примеры кода написаны заново на русском языке для AlashEd Wiki.