wait() vs sleep() в Java: что выбрать для тредов?

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

wait() vs sleep() в Java: битва титанов многопоточности

Представьте, что вы — дирижёр оркестра. У вас есть скрипачи (потоки), которые должны играть слаженно, но иногда им нужно подождать, пока другие закончат партию. В Java для управления такими паузами есть два мощных инструмента: wait() и sleep(). На первый взгляд они похожи — оба заставляют поток «замереть» на время. Но на деле это совершенно разные механизмы, и путать их — верный способ получить deadlock или race condition.

Эта статья — ваш путеводитель по джунглям многопоточности. Мы не просто перечислим различия, а разберём их на живых примерах, чтобы вы навсегда запомнили: sleep — это пауза, а wait — это освобождение ресурса. Поехали!

Что такое sleep()? (Thread.sleep)

Метод Thread.sleep(миллисекунды) — это самый простой способ «усыпить» текущий поток на заданное время. Поток просто отдыхает, не снимая блокировок с объектов. Он как ленивый кот, который дремлет на диване, но продолжает держать лапой игрушку.

Важные факты о sleep():

  • Это статический метод класса Thread.
  • Поток не освобождает мониторы (блокировки), которые он удерживает.
  • После истечения таймера поток переходит в состояние Runnable (готов к выполнению).
  • Может выбросить InterruptedException.

Простой пример:

public class SleepExample {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Начинаем: " + System.currentTimeMillis());
        Thread.sleep(2000); // спим 2 секунды
        System.out.println("Проснулись: " + System.currentTimeMillis());
    }
}

Результат: между двумя выводами будет ровно 2 секунды. Поток просто ждал, ничего не делая.

Что такое wait()? (Object.wait)

Метод wait() — это уже не просто пауза, а механизм межпоточной коммуникации. Он определён в классе Object, и вызывать его можно только внутри синхронизированного блока (synchronized). Когда поток вызывает wait(), он:

  • Освобождает монитор объекта (то есть отпускает блокировку).
  • Переходит в состояние WAITING или TIMED_WAITING (если указан таймаут).
  • Ждёт, пока другой поток не вызовет notify() или notifyAll() на том же объекте.

Это как если бы вы сказали: «Я кладу вилку и жду, пока мне принесут второе блюдо». Вы не просто спите — вы освобождаете место за столом для другого.

Базовый пример:

public class WaitNotifyExample {
    private static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread waiter = new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("Поток-официант ждёт...");
                    lock.wait(); // отпускает lock и ждёт
                    System.out.println("Поток-официант получил уведомление!");
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        });

        Thread notifier = new Thread(() -> {
            synchronized (lock) {
                System.out.println("Поток-повар готовит...");
                try {
                    Thread.sleep(1000); // имитация готовки
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                lock.notify(); // будит ожидающий поток
                System.out.println("Повар уведомил официанта!");
            }
        });

        waiter.start();
        notifier.start();
        waiter.join();
        notifier.join();
    }
}

Вывод: "Поток-официант ждёт..." → "Поток-повар готовит..." → "Повар уведомил официанта!" → "Поток-официант получил уведомление!"

Ключевые различия: таблица сравнения

Давайте сведём всё в наглядную таблицу. Запомните: sleep — для задержки, wait — для синхронизации.

Критерий sleep() wait()
Класс Thread Object
Статический? Да Нет (вызывается на объекте)
Освобождает блокировку? Нет Да
Может быть вызван вне synchronized? Да Нет (вызовет IllegalMonitorStateException)
Пробуждение По таймеру По таймеру или через notify()/notifyAll()
Использование Пауза, задержка, тайминг Межпоточное взаимодействие (producer-consumer)

Когда использовать sleep(), а когда wait()?

Вот несколько сценариев, где каждый из методов будет уместен.

sleep() — идеально для:

  • Имитации длительной операции (например, загрузка файла).
  • Тайм-аутов в циклах опроса (polling).
  • Задержки между повторными попытками (retry logic).
  • Тестирования многопоточности в простых сценариях.

wait() — незаменим для:

  • Шаблона Producer-Consumer (производитель-потребитель).
  • Ожидания определённого условия (например, пока очередь не станет непустой).
  • Эффективного использования ресурсов (поток не тратит CPU на пустые проверки).
  • Реализации блокирующих очередей (как BlockingQueue внутри).

Главные ошибки новичков (и как их избежать)

Ошибка №1: Вызов wait() вне synchronized. Код упадёт с IllegalMonitorStateException. Всегда оборачивайте wait() и notify() в synchronized блок.

Блоги

Рекомендаций по книгам