Я узнаю о сотрудничестве между параллельными задачами, и у меня есть этот вопрос и возможный ответ на него.Я хотел бы убедиться, что я правильно понимаю.
Так что для вызова a.wait()
сначала необходимо выполнить синхронизацию на объекте a
или, если быть более точным, поток должен стать владельцем * 1005.* монитор.Насколько я понимаю, единственная причина, по которой a.wait()
следует вызывать в синхронизированном контексте (вместе с a.notify()
/ a.notifyAll()
), состоит в том, чтобы избежать условия гонки / Потерянная проблема пробуждения ,Но теоретически можно избежать состояния гонки, вызывающего a.wait()
, a.notify()/a.notifyAll()
, путем синхронизации на каком-либо другом объекте, например:
Thread #1:
synchronized(b) {
…
a.wait(); // here a thread owns only one monitor: the monitor of the object stored in the variable b, where a != b
…
}
Thread #2:
synchronized(b) {
…
a.notify(); // the same here
…
}
Ожидаемое поведение: поток # 1 получает мониторb
, входит в критическую секцию, вызывает a.wait()
, затем ждет a
и освобождает монитор b
.После этого поток № 2 получает монитор b
, входит в критическую секцию, вызывает a.notify()
(поток № 1 получает уведомление), выходит из критической секции и освобождает монитор b
, затем поток № 1 получаетмонитор b
снова и продолжает работать.Таким образом, одновременно может работать только одна критическая секция, синхронизированная на b
, и не может возникнуть состояние гонки.Вы можете спросить: «Но как бы wait()
узнал, что объект b
используется в этом случае, чтобы избежать состояния гонки и, следовательно, его монитор должен быть отпущен?».Ну, так оно и не узнает.Но, возможно, метод wait
мог бы взять объект в качестве аргумента, чтобы узнать, какой монитор объекта нужно освободить, например, a.wait(b)
, чтобы ждать на a
и освободить монитор b
.Но такой опции нет: a.wait()
заставляет поток ждать a
и освобождает монитор с точно таким же a
.Период.Вызов методов wait
и notify
в приведенном выше коде приводит к IllegalMonitorStateException .
Согласно этой информации из статьи Википедии , функциональностьМетоды wait
, notify/notifyAll
встроены в монитор каждого объекта, где они интегрированы в функциональность механизма синхронизации на уровне каждого отдельного объекта.Но все же это не объясняет, почему это было сделано именно таким образом.
Мой ответ на вопрос: Перед вызовом метода ожидания объекта поток должен иметь монитор точнотот же объект, потому что, чтобы избежать состояния гонки, этот объект всегда является не только жизнеспособным вариантом, но и лучшим.
Давайте посмотрим, есть ли ситуации, в которых может быть нежелательно синхронизироваться на объекте раньшевызывая его методы wait
, notify/notifyAll
(плюс некоторая соответствующая логика).Синхронизация на объекте блокирует его для других потоков, что может быть нежелательно, если у объекта есть некоторые другие синхронизированные методы (и / или есть некоторые критические секции, синхронизированные на объекте), не имеющие ничего общего с логикой ожидания.В этом случае всегда можно выполнить рефакторинг соответствующего класса (классов), чтобы методы wait
и notify/notifyAll
работали с одним объектом, а другие синхронизированные методы / блоки - с другим.Например, одним из возможных решений может быть создание выделенного объекта специально для логики ожидания (для ожидания и синхронизации), как в примере ниже:
public class A {
private Object lock = new Object(); // an object to synchronize and wait on for some waiting logic
private boolean isReady = false;
public void mOne() throws InterruptedException {
synchronized(lock) { // it blocks the instance of the Object class stored in the variable named lock
while(!isReady) {
lock.wait();
}
}
}
public void mTwo() {
synchronized(lock) { // the same here
isReady = true;
lock.notify();
}
}
synchronized public void mThree() { // it blocks an instance of A
// here is some logic having nothing to do with the above waiting and
// mThree() can run concurrently with mOne() and mTwo()
}
}
Так что нет необходимости синхронизировать некоторые произвольныеобъект, чтобы избежать определенного состояния гонки относительно вызова wait
, notify/notifyAll
методов.Если бы это было разрешено, это только вызвало бы ненужную путаницу.
Это правильно?Или, может быть, я что-то здесь упускаю.Я хотел бы убедиться, что не пропустил ничего важного.
Документация Oracle: wait , notify , notifyAll