В контексте мониторов, где заблокированный поток продолжает выполняться после пробуждения вызовом notify()?
Продолжает ли он выполняться сразу после того, как он вызвал wait()
? Все начинается с самого начала метода обслуживания?
Отрывок из параллельного программирования Стивена Хартли: Язык программирования Java сказал следующее по этому поводу, но я не уверен, что полностью понимаю:
При такой схеме уведомлений невозможно дождаться сигнала в середине метода обслуживания синхронизированного монитора и затем продолжить выполнение внутри метода обслуживания монитора в этот момент после получения сигнала.
Схема уведомлений, на которую она ссылается, является реализацией решения проблемы Readers and Writers с использованием объектов уведомлений.
Вот фрагмент кода этого решения (я только показываю методы, связанные с Readers):
private int numReaders = 0;
private boolean isWriting = false;
private Vector waitingReaders = new Vector();
private Vector waitingWriters = new Vector();
public void startRead(int i) {
Object convey = new Object();
synchronized (convey) {
if (cannotReadNow(convey))
try { convey.wait(); }
catch (InterruptedException e) {}
}
}
private synchronized boolean cannotReadNow(Object convey) {
boolean status;
if (isWriting || waitingWriters.size() > 0) {
waitingReaders.addElement(convey); status = true;
} else {
numReaders++; status = false;
}
return status;
}
public synchronized void endRead(int i) {
numReaders--;
if (numReaders == 0 && waitingWriters.size() > 0) {
synchronized (waitingWriters.elementAt(0)) {
waitingWriters.elementAt(0).notify();
}
waitingWriters.removeElementAt(0);
isWriting = true;
}
}
Причина, по которой я так растерялся, заключается в том, что приведенная выше цитата противоречит практике программирования, показанной в примерах кода из той же книги.
Например, это фрагмент кода решения Readers and Writers, использующего простые мониторы без объектов уведомлений.
public synchronized void startRead(int i) {
long readerArrivalTime = 0;
if (numWaitingWriters > 0 || numWriters > 0) {
numWaitingWriters++;
readerArrivalTime = age();
while (readerArrivalTime >= startWritingReadersTime)
try {wait();}
catch (InterruptedException e) {}
numWaitingReaders--;
}
numReaders++;
}
Если поток не может продолжить выполнение там, где он был заблокирован вызовом wait()
Почему цикл while используется для проверки состояния? Если каждый поток заблокирован, а затем снова получает доступ к монитору с помощью вызова startRead()
должен начинаться с начала этого метода, как, кажется, предполагает приведенная выше цитата, не достаточно ли оператора if для проверки условия?
Кроме того, как все это объясняет следующую цитату, которая в книге следует сразу же после цитаты выше:
Чтобы избежать взаимоблокировки, поток должен оставить синхронизированный метод с оператором возврата, прежде чем ждать внутри объекта уведомления.
1 ответ
Если я понял этот вопрос... Попробуйте это:
public synchronized void startRead(int i) {
long readerArrivalTime = 0;
if (numWaitingWriters > 0 || numWriters > 0) {
numWaitingWriters++;
readerArrivalTime = age();
while (readerArrivalTime >= startWritingReadersTime)
try {wait();}
catch (InterruptedException e) {}
numWaitingReaders--;
}
numReaders++;
}
public synchronized void endRead(int i) {
numReaders--;
if (numReaders == 0 && waitingWriters.size() > 0) {
notify();
waitingWriters.removeElementAt(0);
isWriting = true;
}
}
Методы concreteObject.wait/notify/notifyAll можно вызывать только из синхронизированного блока (concreteObject). Если вы вызываете их без concreteObject (просто wait () или notify()), это то же самое, что this.wait () или this.notify ().
Синхронизированные нестатические методы такие же, как синхронизированные (это) блоки.