Главная » Java » Методы wait и notify

0

Механизм блокировки решает проблему с наложением потоков, однако хотелось бы, чтобы потоки могли обмениваться информацией друг с другом. Для этого существует два метода: wait и notify. Метод wait позволяет потоку дождаться выполнения определенного условия, а метод notify извещает все ожидающие потоки о наступлении некоторого события.

определены в классе Object и наследуются всеми классами. Они, подобно блокировке, относятся к конкретным объектам. При выполнении wait вы ожидаете, что некоторый поток известит (notify) о наступлении события тот самый объект, в котором происходит ожидание.

Существует стандартная конструкция, которой следует пользоваться в работе с wait и notify. Поток, ожидающий события, должен включать что- нибудь похожее на следующий фрагмент:

synchronized void doWhenCondition() {

while (!условие)

wait();

… Действия, выполняемые при выполнении условия …

}

Здесь следует обратить внимание на несколько аспектов:

Все действия выполняются внутри синхронизированного метода. Это

необходимо — в противном случае нельзя быть уверенным в содержимом объекта. Например, если метод не синхронизирован,  то после выполнения оператора while нет гарантии, что условие окажется истинным — ситуация могла быть изменена другим потоком.

Одно из важных свойств определения wait заключается в том, что во время приостановки потока происходит атомарное (atomic) снятие блокировки с объекта. Когда говорят об атомарной приостановке потока и снятии блокировки, имеется в виду, что эти операции происходят вместе и не могут отделяться друг от друга. В противном случае снова возникла бы “гонка”: извещение могло бы придти после снятия блокировки, но перед приостановкой потока. В этом случае извещение никак не влияет на работу потока и фактически теряется. Когда поток возобновляет работу после получения извещения, происходит повторная блокировка.

Условие всегда должно проверяться внутри цикла. Никогда не следует полагать, что возобновление работы потока означает выполнение условия. Другими словами, не заменяйте while на if.

С другой стороны, метод notify вызывается методами, изменяющими данные, которые могут ожидаться другим потоком.

synchronized void changeCondition() {

… изменить величину, используемую при проверке условия … notify();

}

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

Приводимый ниже класс реализует концепцию очереди. Он содержит методы, которые используются для помещения элементов в очередь и их удаления:

class Queue {

// первый и последний элементы очереди

Element head, tail;

public synchronized void append(Element p) {

if (tail == null)

head = p;

else

tail.next = p;

p.next = null;

tail = p;

notify(); // сообщить ожидающим потокам о новом элементе

}

public synchronized Element get() {

try {

while(head == null)

wait();  // ожидать появления элемента

} catch (InterruptedException e) {

return;

}

Element p = head;    // запомнить первый элемент

head = head.next;    // удалить его из очереди

if (head == null)    // проверить, не пуста ли очередь

tail = null;

return p;

}

}

Такая реализация очереди во многом напоминает ее воплощение в однопоточной системе. Отличий не так уж много: методы синхронизированы;  при занесении нового элемента в очередь происходит извещение ожидающих потоков; вместо того чтобы возвращать null для пустой очереди, метод get ждет, пока какой-нибудь другой поток занесет элемент в очередь. Как занесение, так и извлечение элементов очереди может осуществляться несколькими потоками (а не обязательно одним).

Источник: Арнольд К., Гослинг Д. – Язык программирования Java (1997)

По теме:

  • Комментарии