logo search
Языки программирования

18.6. Параллелизм

Ada — один из немногих языков, в которых поддержка параллелизма включе­на в сам язык, в отличие от делегирования этих функций операционной сис­теме. Язык Java продолжает идеологию языка Ada в отношении пере­носимости параллельных программ вне зависимости от операционных сис­тем. Важное применение параллелизма в Java — программирование серверов: каждый запрос клиента заставляет сервер порождать (spawn) новый процесс для выполнения этого запроса.

Параллельно исполняемые конструкции Java называются нитями (thread). Собственно в параллелизме нет никаких существенных различий между нитью и тем, что называют стандартным термином процесс; отличие состоит в реализации, ориентированной на выполнение многих нитей внутри одного и того же адресного пространства. Для разработки и анализа параллельных ал­горитмов используется та же самая модель, что и в гл. 12 ,— чередующееся вы­полнение атомарных инструкций процессов.

Класс Java, который наследуется из класса Thread, объявляет только но­вый тип нить. Чтобы реально создать нить, нужно запросить память, а затем вызвать функцию start. В результате будет запущен метод run внутри нити:

class My_Thread extends Thread

{

public void run(){...}; // Вызывается функцией start

Java

}

My_Thread t = new My_Thread(); // Создать нить

t.start(); //Активизировать нить

Одна нить может явно создавать, уничтожать, блокировать и разблокировать другую нить.

Эти конструкции аналогичны конструкциям в языке Ada, которые позво­ляют определить тип task (задача) и затем динамически создавать задачи.

Синхронизация

Java поддерживает форму синхронизации аналогично мониторам (см. раздел 12.4). Класс может содержать методы, специфицированные как synchronized (синхронный). С каждым таким объектом в классе связывается блокировка-пропускник (lock), которая гарантирует, что только одна нить в данный момент может выполнять синхронный метод в объекте. Следующий пример показывает, как объявить монитор для защиты разделяемого ресурса от одно­временного использования несколькими нитями:

class Monitor

{

synchronized public void seize() throws InterruptedException

{

while (busy) wait();

Java

busy = true;

}

synchronized public void release()

{

busy = false;

notify();

}

private boolean busy - false

}

Монитор использует булеву переменную, которая указывает состояние ресур­са. Если две нити пытаются выполнить метод seize в мониторе, то только одна из них пройдет пропускник и выполнится. Эта нить установит переменную busy (занято) в состояние true (истина) и перейдет к использованию ресурса. По завершении метода пропускник откроется, и другая нить сможет выполнить метод seize. Теперь, однако, переменная busy будет иметь значение false (ложь). Вместо ненужных затрат времени ЦП на непрерывную проверку пере­менной нить предпочитает освободить ЦП с помощью запроса wait (ждать). Когда первая нить заканчивает использование разделяемого ресурса, она вы­зывает notify (уведомление), которое позволит ожидающей нити снова возоб­новить выполнение синхронного метода.

Конструкции Java для параллелизма достаточно просты. Нет ничего по­хожего на сложные рандеву Ada для прямой связи процесс-процесс. Даже по сравнению с защищенными объектами конструкции Java относительно слабы:

• Барьер защищенного объекта автоматически перевычисляется всякий раз, когда его значение может измениться; в Java нужно явно программи­ровать циклы.

• Java предоставляет простую блокировку-пропускник для каждого объек­та; в Ada заводится очередь для каждого входа. Таким образом, если не­сколько нитей Java ожидают входа в синхронный объект, вы не можете знать, какой из них будет запущен по notity, поэтому трудно программи­ровать алгоритмы с гарантированно ограниченным временем ожидания.