← Назад к вопросам

Какие знаешь ситуации при которых вызывают wait()?

1.8 Middle🔥 111 комментариев
#Многопоточность#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Ситуации использования wait() в Java

wait() — это метод для синхронизации потоков, который позволяет одному потоку дождаться выполнения условия другим потоком. Это фундаментальный механизм для многопоточного программирования.

Основные концепции

wait(), notify() и notifyAll() работают исключительно внутри synchronized блоков. Это часть Monitor Pattern — основы синхронизации в Java.

// Структура всегда одинакова
synchronized (lock) {
    while (!condition) {
        lock.wait();  // освобождает lock и ждёт
    }
    // condition выполнено
}

1. Producer-Consumer Pattern

Одна из самых классических ситуаций. Производитель создаёт данные, потребитель их обрабатывает.

public class ProducerConsumer {
    private final Object lock = new Object();
    private final Queue<String> buffer = new LinkedList<>();
    private final int MAX_SIZE = 10;
    
    public void produce(String item) throws InterruptedException {
        synchronized (lock) {
            // ждём, пока буфер не освободится
            while (buffer.size() == MAX_SIZE) {
                lock.wait();
            }
            
            buffer.offer(item);
            System.out.println("Produced: " + item);
            lock.notifyAll(); // уведомляем потребителей
        }
    }
    
    public String consume() throws InterruptedException {
        synchronized (lock) {
            // ждём, пока буфер не наполнится
            while (buffer.isEmpty()) {
                lock.wait();
            }
            
            String item = buffer.poll();
            System.out.println("Consumed: " + item);
            lock.notifyAll(); // уведомляем производителей
            return item;
        }
    }
}

2. Waiter Pattern для асинхронных результатов

Когда один поток должен дождаться результата от другого потока.

public class FutureResult<T> {
    private T result;
    private Exception exception;
    private boolean done = false;
    
    public synchronized void set(T result) {
        this.result = result;
        this.done = true;
        notifyAll(); // пробуждаем ждущие потоки
    }
    
    public synchronized void setException(Exception e) {
        this.exception = e;
        this.done = true;
        notifyAll();
    }
    
    public synchronized T get() throws InterruptedException, Exception {
        while (!done) {
            wait(); // ждём результата
        }
        
        if (exception != null) {
            throw exception;
        }
        return result;
    }
}

3. Worker Pool / Thread Pool

Несколько рабочих потоков ждут задач из очереди.

public class SimpleThreadPool {
    private final Object taskLock = new Object();
    private final Queue<Runnable> taskQueue = new LinkedList<>();
    private final int poolSize;
    private volatile boolean shutdown = false;
    
    public SimpleThreadPool(int poolSize) {
        this.poolSize = poolSize;
        
        for (int i = 0; i < poolSize; i++) {
            new Thread(new Worker()).start();
        }
    }
    
    public void submit(Runnable task) throws InterruptedException {
        synchronized (taskLock) {
            if (shutdown) {
                throw new IllegalStateException("Pool is shutdown");
            }
            taskQueue.offer(task);
            taskLock.notifyAll(); // пробуждаем ждущих workers
        }
    }
    
    private class Worker implements Runnable {
        @Override
        public void run() {
            while (true) {
                Runnable task;
                
                synchronized (taskLock) {
                    while (taskQueue.isEmpty() && !shutdown) {
                        try {
                            taskLock.wait(); // ждём задачи
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            return;
                        }
                    }
                    
                    if (shutdown && taskQueue.isEmpty()) {
                        return; // выходим из pool
                    }
                    
                    task = taskQueue.poll();
                }
                
                if (task != null) {
                    task.run();
                }
            }
        }
    }
}

4. Barrier / Встреча нескольких потоков

Несколько потоков ждут друг друга для синхронизации.

public class SimpleBarrier {
    private final Object lock = new Object();
    private final int parties; // сколько потоков должно встретиться
    private int arrived = 0;
    private int generation = 0;
    
    public SimpleBarrier(int parties) {
        this.parties = parties;
    }
    
    public void await() throws InterruptedException {
        synchronized (lock) {
            int currentGeneration = generation;
            arrived++;
            
            if (arrived == parties) {
                // все пришли
                generation++;
                arrived = 0;
                lock.notifyAll(); // просыпаемся все вместе
            } else {
                // ждём остальных
                while (generation == currentGeneration) {
                    lock.wait();
                }
            }
        }
    }
}

5. Паттерн Condition Variable

Ожидание конкретного условия, а не просто события.

public class DataHolder {
    private Object data;
    private boolean ready = false;
    private final Object lock = new Object();
    
    public void setData(Object data) {
        synchronized (lock) {
            this.data = data;
            this.ready = true;
            lock.notifyAll(); // уведомляем, что данные готовы
        }
    }
    
    public Object getData() throws InterruptedException {
        synchronized (lock) {
            // ждём именно условия ready == true
            while (!ready) {
                lock.wait();
            }
            return data;
        }
    }
}

6. Graceful Shutdown

Управление корректным завершением потоков.

public class GracefulShutdown {
    private final Object shutdownLock = new Object();
    private volatile boolean shutdownRequested = false;
    
    public void requestShutdown() {
        synchronized (shutdownLock) {
            shutdownRequested = true;
            shutdownLock.notifyAll();
        }
    }
    
    public void run() {
        while (true) {
            synchronized (shutdownLock) {
                while (!shutdownRequested) {
                    try {
                        shutdownLock.wait(); // ждём сигнала завершения
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        return;
                    }
                }
            }
            
            // выполняем graceful shutdown
            cleanupResources();
            break;
        }
    }
    
    private void cleanupResources() {
        // закрываем соединения, сохраняем состояние и т.д.
    }
}

7. Retry Pattern с exponential backoff

Попытка повторно выполнить операцию с ожиданием между попытками.

public class RetryWithWait {
    public Object executeWithRetry(Callable<Object> operation) 
            throws Exception {
        int maxRetries = 3;
        int attempt = 0;
        long baseDelay = 1000; // 1 секунда
        
        while (attempt < maxRetries) {
            try {
                return operation.call();
            } catch (Exception e) {
                attempt++;
                
                if (attempt >= maxRetries) {
                    throw e;
                }
                
                long delay = baseDelay * (long) Math.pow(2, attempt - 1);
                synchronized (this) {
                    this.wait(delay); // ждём перед повтором
                }
            }
        }
        
        return null;
    }
}

Когда НЕ использовать wait()

Современные альтернативы намного лучше для большинства случаев:

// ❌ Избегай старых приёмов с wait()
public void oldWayToSleep() throws InterruptedException {
    synchronized (this) {
        this.wait(1000);
    }
}

// ✅ Используй современный подход
public void modernWayToSleep() {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}

// ✅ Или реактивные инструменты
CompletableFuture.delayedExecutor(1, TimeUnit.SECONDS)
    .execute(() -> System.out.println("После ожидания"));

// ✅ Или CountDownLatch
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        // работа
        latch.countDown();
    }).start();
}
latch.await(); // ждём, пока все потоки закончат

Лучшие практики

  1. Всегда используй while вместо if при проверке условий в wait()
  2. Всегда вызывай notifyAll() вместо notify() чтобы избежать потери сигналов
  3. Предпочитай современные инструменты: CyclicBarrier, CountDownLatch, Semaphore, Future
  4. Избегай старых приёмов с wait/notify, где возможно
  5. Профилируй deadlocks и race conditions

Заключение

wait() — это мощный, но низкоуровневый инструмент. Для 90% случаев лучше использовать готовые примитивы синхронизации из java.util.concurrent. wait() встречается в собеседованиях потому что демонстрирует понимание многопоточности на глубоком уровне.
Какие знаешь ситуации при которых вызывают wait()? | PrepBro