Thread не будится после вызова notifyAll()

Есть три простых thread. два запускаются в run, третий будит по очереди.

        Main main = new Main(); // экземпляр, по которому вызываем методы и синхронизируемся

    Thread thread1 = new Thread(() -> main.methodA());
    Thread thread2 = new Thread(() -> main.methodA());
    Thread thread3Notify = new Thread(() -> main.methodB());

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

  public synchronized void methodA()   {
        System.out.println("I enter methodA" + Thread.currentThread());
                this.wait();
        System.out.println("I am awake " + Thread.currentThread());
}

Метод, которым я хотел разбудить два thread по очереди.

    public synchronized void methodB() {
    this.notifyAll();
    sleep(1000);
    this.notifyAll();
}

Но вот проблема: Я вижу это в консоли

I enter methodAThread[Thread-0,5,main]
I enter methodAThread[Thread-1,5,main]
I am awake Thread[Thread-0,5,main]

Т.е. methodB будит только один первый поток, а второй зависает в methodA навечно. Не важно, сколько раз я вызову this.notifyAll()

Пытался сам понять, но не выходит.


Ответы (1 шт):

Автор решения: Serodv

Постараюсь объяснить пошагово. Запускаются три потока и, как мы знаем, порядок их запуска произвольный и ничем не регулируется. В данном случае он такой - 1,3,2. Первый поток заходит в synchronized метод, блокирует монитор (остальные ждут, если уже запустились), печатает сообщение и доходит до wait(), который этот самый монитор и освобождает. Вторым в свой метод заходит третий поток, блокирует монитор (т.е. второй поток будет ждать разблокировки на своём методе, когда дойдёт), бросает notifyAll() + пауза + notifyAll(). Теперь важно - пока третий поток не освободит монитор (по сути не закончит выполнение своего метода), никакие другие потоки не активизируются. Дальше третий поток заканчивает работу метода и освобождает монитор. На мониторе висят 2 заблокированных потока - первый на методе wait() (причём на этот wait() прилетело два уведомления) и второй на входе в метод. Приоритет выполнения отдаётся второму потоку - он печатает второе сообщение, доходит до wait() и освобождает монитор. Выполнение передаётся первому потоку, он печатает сообщение и заканчивает работу. Второй поток продолжает висеть на своём wait() и ждать нотификации, которая уже никогда не придёт.

Вы можете поэксперементировать с очерёдностью запуска потоков используя паузы при их старте при этом получая различные результаты.

→ Ссылка