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