Понимание разницы между notify() и notifyAll() в Java

В многопоточном программировании Java предоставляет два метода: notify()и notifyAll(), которые позволяют потокам взаимодействовать и синхронизировать свои действия. Хотя оба метода служат одной и той же цели, между ними существуют ключевые различия, о которых необходимо знать разработчикам. В этой статье мы рассмотрим различия между notify()и notifyAll(), предоставим примеры кода и обсудим лучшие варианты их использования.

1. уведомить()

Метод notify()является частью класса Objectи используется для пробуждения одного потока, ожидающего на мониторе того же объекта. Когда вызывается notify(), он сигнализирует одному случайно выбранному потоку о возобновлении выполнения. Если в ожидании находятся несколько потоков, пробуждается только один из них, и выбор того, какой поток, зависит от реализации JVM.

Вот пример фрагмента кода, демонстрирующий использование notify():

public class Notifier {
    private Object lock = new Object();
    public void doNotify() {
        synchronized (lock) {
            lock.notify();
        }
    }
}
public class Waiter implements Runnable {
    private Object lock;
    public Waiter(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized (lock) {
            try {
                System.out.println("Waiter thread waiting...");
                lock.wait();
                System.out.println("Waiter thread resumed...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Main {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        Thread waiterThread = new Thread(new Waiter(lock));
        waiterThread.start();
        Thread.sleep(1000); // Let the waiter thread start waiting
        Notifier notifier = new Notifier();
        notifier.doNotify();
    }
}

В этом примере поток Waiterожидает, пока Notifierуведомит его. После того как Notifierвызывает notify(), поток Waiterвозобновляет выполнение.

2. уведомитьВсе()

Метод notifyAll(), также являющийся частью класса Object, используется для пробуждения всех потоков, ожидающих на мониторе одного и того же объекта. При вызове notifyAll()все потоки пробуждаются и конкурируют за монитор объекта.

Вот пример фрагмента кода, демонстрирующий использование notifyAll():

public class Notifier {
    private Object lock = new Object();
    public void doNotifyAll() {
        synchronized (lock) {
            lock.notifyAll();
        }
    }
}
public class Waiter implements Runnable {
    private Object lock;
    public Waiter(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized (lock) {
            try {
                System.out.println("Waiter thread waiting...");
                lock.wait();
                System.out.println("Waiter thread resumed...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Main {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        Thread waiterThread1 = new Thread(new Waiter(lock));
        Thread waiterThread2 = new Thread(new Waiter(lock));
        waiterThread1.start();
        waiterThread2.start();
        Thread.sleep(1000); // Let the waiter threads start waiting
        Notifier notifier = new Notifier();
        notifier.doNotifyAll();
    }
}

В этом примере оба потока Waiterждут, пока Notifierуведомит их. После того как Notifierвызывает notifyAll(), все потоки Waiterвозобновляют выполнение и конкурируют за монитор объекта.

Лучшие примеры использования:

  • Используйте notify(), когда вам нужно разбудить только один ожидающий поток, и не имеет значения, какой именно.
  • Используйте notifyAll(), когда вам нужно разбудить все ожидающие потоки или если вы хотите реализовать поведение, подобное широковещательному.