Какие знаешь ситуации при которых вызывают wait()?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Ситуации использования 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(); // ждём, пока все потоки закончат
Лучшие практики
- Всегда используй while вместо if при проверке условий в wait()
- Всегда вызывай notifyAll() вместо notify() чтобы избежать потери сигналов
- Предпочитай современные инструменты: CyclicBarrier, CountDownLatch, Semaphore, Future
- Избегай старых приёмов с wait/notify, где возможно
- Профилируй deadlocks и race conditions
Заключение
wait() — это мощный, но низкоуровневый инструмент. Для 90% случаев лучше использовать готовые примитивы синхронизации из java.util.concurrent. wait() встречается в собеседованиях потому что демонстрирует понимание многопоточности на глубоком уровне.