Согласование работы нескольких подпроцессов
Возможность создания многопоточных программ заложена в язык Java с самого его создания. В каждом объекте есть три метода wait () и один метод notify (), позволяющие приостановить работу подпроцесса с этим объектом, позволить другому подпроцессу поработать с объектом, а затем уведомить (notify) первый подпроцесс о возможности продолжения работы. Эти методы определены прямо в классе object и наследуются всеми классами.
С каждым объектом связано множество подпроцессов, ожидающих доступа к объекту (wait set). Вначале этот "зал ожидания" пуст.
Основной метод wait (long millisec) приостанавливает текущий подпроцесс this, работающий с объектом, на millisec миллисекунд и переводит его в "зал ожидания", в множество ожидающих подпроцессов. Обращение к этому методу допускается только в синхронизированном блоке или методе, чтобы быть уверенными в том, что с объектом работает только один подпроцесс. По истечении millisec или после того, как объект получит уведомление методом notify о, подпроцесс готов возобновить работу. Если аргумент millisec равен о, то время ожидания не определено и возобновление работы подпроцесса возможно только после того, как объект получит уведомление методом notify().
Отличие данного метода от метода sleep () в том, что метод wait () снимает блокировку с объекта. С объектом может работать один из подпроцессов из "зала ожидания", обычно тот, который ждал дольше всех, хотя это не гарантируется спецификацией JLS.
Второй метод wait () эквивалентен wait (0). Третий метод wait (long millisec, int nanosec) уточняет задержку на nanosec наносекунд, если их сумеет отсчитать операционная система.
Метод notify () выводит из "зала ожидания" только один, произвольно выбранный подпроцесс. Метод notifyAll() выводит из состояния ожидания все подпроцессы. Эти методы тоже должны выполняться в синхронизированном блоке или методе.
Как же применить все это для согласованного доступа к объекту? Как всегда, лучше всего объяснить это на примере.
Обратимся снова к схеме "поставщик-потребитель", уже использованной в главе 15. Один подпроцесс, поставщик, производит вычисления, другой, потребитель, ожидает результаты этих вычислений и использует их по мере поступления. Подпроцессы передают информацию через общий экземпляр st класса store.
Работа этих подпроцессов должна быть согласована. Потребитель обязан ждать, пока поставщик не занесет результат вычислений в объект st, а поставщик должен ждать, пока потребитель не возьмет этот результат.
Для простоты поставщик просто заносит в общий объект класса store целые числа, а потребитель лишь забирает их.
В листинге 17.6 класс store не обеспечивает согласования получения и выдачи информации. Результат работы показан на рис. 17.4.