Пожалуй, самое интересное в методах wait(), notify() и notifyAU() — их принадлежность к общему классу Object, а не к классу потоков Thread. Хотя это и кажется немного нелогичным — размещение чего-то, относящегося исключительно к механизму потоков, в общем базовом классе — на самом деле это решение совершенно оправдано, поскольку означенные методы манипулируют блокировками, которые являются частью любого объекта. В результате ожидание (wait()) может использоваться в любом синхронизированном методе, независимо от того, наследует ли класс от Thread или реализует Runnable. Вообще говоря,
Вы можете «попросить» объект провести операции с помощью его собственного объекта блокировки. Для этого необходимо сначала захватить блокировку для данного объекта. Например, если вы хотите вызвать notifyAll() для объекта х, то должны сделать это в синхронизируемом блоке, устанавливающем блокировку для х:
synchronized(x) { х notifyAllО;
}
Рассмотрим простой пример. В программе WaxOMatic.java задействованы два процесса: один наносит восковую пасту на автомашину (Саг), а другой полирует ее. Задача полировки не может приступить к работе до того, как задача нанесения пасты завершит свою операцию, а задача нанесения пасты должна ждать завершения полировки, чтобы наложить следующий слой пасты. Оба класса, WaxOn и WaxOff, используют объект Саг, который приостанавливает и возобновляет задачи в ожидании изменения условия:
//: concurrency/waxomatic/WaxOMatic.java // Простейшее взаимодействие задач, package concurrency.waxomatic, import java.util concurrent.*; import static net.mindview util.Print.*;
class Car {
private boolean waxOn = false; public synchronized void waxedО {
waxOn = true; // Готово к обработке notifyAll О;
}
public synchronized void buffedО {
waxOn = false; // Готово к нанесению очередного слоя notifyAll О;
}
public synchronized void waitForWaxingO throws InterruptedException { while(waxOn == false) waitO;
}
public synchronized void waitForBuffingO throws InterruptedException {
while(waxOn == true)
waitO;
}
}
class WaxOn implements Runnable { private Car car;
public WaxOn(Car c) { car = c; } public void run() { try {
while( IThread interruptedO) { printnbC'Wax On! "); Ti mellni t. MILLISECONDS. si eep( 200); car.waxedO; car.waitForBuffingO;
}
} catchdnterruptedException e) {
printC'Exiting via interrupt");
}
print("Ending Wax On task");
}
}
class WaxOff implements Runnable { private Car car;
public WaxOff(Car c) { car = c; } public void runO { try {
while(IThread.interruptedO) { car.waitForWaxingO; printnbC'Wax Off! "); Ti mellni t. MILLI SECONDS. s 1 eep (200); car.buffedO;
}
} catchdnterruptedException e) {
printC'Exiting via interrupt");
}
printC'Ending Wax Off task");
public class WaxOMatic {
public static void main(String[] args) throws Exception { Car car = new CarO;
ExecutorService exec = Executors.newCachedThreadPoolО;
exec.execute(new WaxOff(car));
exec.execute(new WaxOn(car));
TimeUnit SECONDS.sieep(5); // Небольшая задержка. .
exec.shutdownNowO; // Прерывание всех задач
}
} /* Output:
Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Exiting via interrupt Ending Wax On task Exiting via interrupt Ending Wax Off task *///:-
Класс Саг содержит одну логическую переменную waxOn, которая описывает текущее состояние процесса полировки.
Метод waitForWaxing() проверяет флаг waxOn, и, если он равен false, вызывающая задача приостанавливается вызовом wait(). Очень важно, что это происходит в синхронизированном методе. При вызове wait() поток приостанавливается, а блокировка снимается. Последнее принципиально, потому что для безопасного изменения состояния объекта (например, для присваивания waxOn значения true, без чего приостановленная задача не сможет продолжить работу) блокировка должна быть доступна для другой задачи. В нашем примере при вызове другой задачей метода waxed(), указывающего, что пришло время что-то сделать, для задания истинного значения waxOn необходимо установить блокировку. Затем waxed() вызывает notifyAll(); задача, приостановленная вызовом wait(), активизируется. Для этого нужно сначала заново получить блокировку, освобожденную при входе в wait(). Задача не активизируется, пока блокировка не станет доступной.