В чем разница между методами wait() и sleep() в Java? Примеры с кодом
Основные различия между wait() и sleep() в Java
Методы wait() и sleep() действительно оба приостанавливают выполнение потока, но имеют принципиально разное назначение и поведение. Вот ключевые отличия:
- Принадлежность к классам:
sleep()— статический метод классаThread, аwait()— метод классаObject - Синхронизация:
wait()обязательно должен вызываться внутри синхронизированного блока (synchronized), иначе будетIllegalMonitorStateException.sleep()может вызываться где угодно - Освобождение блокировок:
wait()освобождает монитор объекта (блокировку), позволяя другим потокам войти в синхронизированные блоки.sleep()не освобождает никакие блокировки - Пробуждение:
sleep()просыпается автоматически по истечении таймаута.wait()можно разбудить вызовомnotify()илиnotifyAll()из другого потока, либо по таймауту - Цель использования:
sleep()— для паузы на фиксированное время.wait()— для межпотокового взаимодействия (Producer-Consumer паттерн)
Пример корректного использования wait() и notify()
Исправим ваш пример Producer-Consumer с помощью wait() и notify():
import java.util.LinkedList;
import java.util.Queue;
class SharedBuffer {
private Queue<Integer> buffer = new LinkedList<>();
private final int MAX_SIZE = 5;
public synchronized void produce(int value) throws InterruptedException {
while (buffer.size() == MAX_SIZE) {
wait(); // Ждем, пока потребитель освободит место
}
buffer.add(value);
System.out.println("Произведено: " + value);
notify(); // Будим ожидающий поток-потребитель
}
public synchronized int consume() throws InterruptedException {
while (buffer.isEmpty()) {
wait(); // Ждем, пока производитель добавит данные
}
int value = buffer.poll();
System.out.println("Потреблено: " + value);
notify(); // Будим ожидающий поток-производитель
return value;
}
}
class Producer extends Thread {
private SharedBuffer buffer;
Producer(SharedBuffer b) { buffer = b; }
public void run() {
for (int i = 0; i < 10; i++) {
try {
buffer.produce(i);
Thread.sleep(100); // sleep() для имитации работы
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
class Consumer extends Thread {
private SharedBuffer buffer;
Consumer(SharedBuffer b) { buffer = b; }
public void run() {
for (int i = 0; i < 10; i++) {
try {
buffer.consume();
Thread.sleep(150); // sleep() для имитации работы
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
Ключевые моменты в этом примере:
wait()вызывается в циклеwhile(а не вif), чтобы избежать ложного пробуждения (spurious wakeup)notify()будит только один поток. Если нужно разбудить все — используйтеnotifyAll()sleep()используется только для симуляции времени работы, а не для синхронизации- Все методы
produce()иconsume()синхронизированы, иwait()вызывается внутри них
Когда что использовать?
- sleep() — когда нужно просто подождать фиксированное время (таймаут, задержка между операциями)
- wait() + notify() — когда потоки должны координировать свою работу: один ждет, пока другой выполнит определенное условие
Запомните главное правило: wait() отдает блокировку и ждет сигнала, sleep() держит блокировку и просто спит.
Дополнительные нюансы: прерывание и таймауты
Оба метода могут выбросить InterruptedException, но реагируют на прерывание по-разному:
- sleep() — при прерывании поток просыпается немедленно и выбрасывает исключение. Блокировки, которые он держал, остаются захваченными
- wait() — при прерывании поток также просыпается, но ему нужно повторно захватить монитор (блокировку) перед тем, как выбросить исключение
Варианты с таймаутом
У wait() есть перегруженная версия wait(long timeout), которая работает как комбинация sleep() и wait():
synchronized (lock) {
// Ждем не более 1 секунды или пока не вызовут notify()
lock.wait(1000);
// Проснулись либо по таймауту, либо по notify()
}
В отличие от sleep(), wait(timeout) освобождает блокировку на время ожидания. Если вы хотите просто подождать с освобождением блокировки — используйте wait(timeout), если без освобождения — sleep().
Практический совет
Никогда не используйте sleep() для синхронизации потоков — это приведет к race conditions и трудноуловимым багам. Для координации потоков всегда используйте wait()/notify() или высокоуровневые конструкции из java.util.concurrent (например, BlockingQueue).