Операционные системы "тонких" клиентов

Методическое пособие - Компьютеры, программирование

Другие методички по предмету Компьютеры, программирование

твенную кучу).

Но Java VM не выполняет действий по планированию нитей на выполнение. Для этого библиотечные методы Java обращаются к ОС, используя API той ОС, в среде которой работает Java VM.

Для нитей в Java предусмотрено управление приоритетами (методы getPriority(), setPriority()), однако, и здесь Java использует механизмы управления приоритетами ОС. Так, уже классическим для учебников по Java является пример аплета, в котором по экрану "наперегонки" движутся несколько объектов, движение каждого осуществляется в отдельной нити и с собственным приоритетом. Этот пример весьма наглядно демонстрируется в средах, например, OS/2 и Linux, но выглядит не очень убедительным в среде Windows 95/98, так как приоритет нити не слишком влияет на скорость движения объекта - примерно так, как показано на рисунке 13.5.

 

Рисунок 13.5 "Гонки" в разных операционных средах (движение справа налево).

 

Любая нить может быть сделана нитью-"демоном". Нить-"демон" продолжает выполняться даже после окончания той нити, в которой она была создана. Нить становится "демоном" при ее создании только в том случае, если она создается из нити-"демона". Программа может изменить состояния нити при помощи метода setDaemon() класса Thread. Выполнение любой программы Java VM начинает с единственной нити (в которой вызывается метод main()), и эта нить запускается не как "демон". Java VM продолжает существовать, пока не завершатся все нити не-"демоны".

В языке Java имеется также класс ThreadGroup - группа нитей, которая может управляться совместно.

Если в Java предусмотрены нити, то, естественно, должны быть предусмотрены и средства синхронизации и взаимного исключения при параллельной работе нитей. Основным средством синхронизации и взаимного исключения в Java является ключевое слово synchronized, которое может употребляться перед каким-либо программным блоком. Ключевое слово synchronized определяет невозможность использования программного блока двумя или более нитей одновременно. В Java synchronized-блоки называются мониторами и их фактическая тождественность мониторам Хоара, описанным в разделе 8.8 части I, очевидна. В зависимости от деталей способа употребления synchronized может работать как:

защищенная (guard - см. раздел 8.8 части I) процедура - в том случае, если synchronized-блок представляет собой целый метод, однако, в отличие от описанных нами guard-процедур одновременное вхождение в разные synchronized-методы возможно;

анонимные скобки критической секции - в том случае, если synchronized-блок является просто программным блоком;

скобки критической секции с защитой выбранного ресурса - в том случае, если после ключевого слова synchronized указывается в скобках ссылка на объект.

В первоначальной версии языка Java для класса Thread предусмотрены методы:

resume() - приостановить выполнение нити;

suspend() - возобновить выполнение нити;

yeld() - сделать паузу в выполнении нити, чтобы дать возможность выполниться другой нити;

join() - ожидать завершения нити.

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

Класс Object имеет три метода:

wait() - ожидать уведомления об этом объекте;

notify() - послать уведомление одной из нитей, ждущих уведомления об этом объекте;

notifyAll() - послать уведомление всем из нитям, ждущим уведомления об этом объекте.

Поскольку класс Object является корнем иерархии классов, объекты всех - стандартных и пользовательских - классов являются его подклассами и наследуют эти методы. Эти методы аналогичны операциям wait и signal, описанным нами в разделе 8.7 части I. Реализация, например, общего (с возможным значением, большим 1) семафора с использованием этих средств будет выглядеть следующим образом:

//** семафор реализуется в виде класса Semaphore

public class Semaphore

{

// значение семафора

private int Semaphore_value;

//** пустой конструктор семафора, по умолчанию начальное значение семафора - 0

public Semaphore()

{

this(0);

}

//** конструктор с параметром - начальным значением семафора,

// если задано отрицательное значение, устанавливается начальное значение 0

public Semaphore(int val)

{

if (val < 0) Semaphore_value = 0

else Semaphore_value = val;

}

//** V-операция. V- и P-операции объявлены synchronized,

// чтобы исключить одновременное выполнение их двумя или более нитями

public synchronized void V()

{

// возможно пробуждает нить, ожидающую у семафора

if (Semaphore_value == 0) this.notify();

// увеличивает значение семафора

Semaphore_value++;

}

//** P-операция

public synchronized void P() throws InterruptedException

// исключение может выбрасываться в wait

{

// если значение семафора равно 0, блокирует нить

while (counter == 0) this.wait();

// уменьшает значение семафора

Semaphore_value--;

}

}

 

В Java VM с каждым объектом связывается замок (lock) и список ожидания (wait set).

В спецификациях байт-кода Java имеются специальные команды monitorenter и monitorexit, устанавливающие и снимающие замок. Java VM, входя в synchronized-блок, пытается выполнить операцию установки замка и не продолжает выполнения нити, пока операция не будет выполнена. При выходе из synchronized-блока выполняется операция снятия замка.

Список ожидания используется методами wait(), notify(), notifyAll(). Он представляет собой список нитей, ожидающих уведомления о данном объекте. Названные операции работают с этим списком очевидным образом.

Следует отметить, что многопоточность, заложенная в языке Java, имеет большие перспективы. Изна