Выполнение критического интервала начинается только после полу-
чения потоком монопольного доступа к соответствующему об®екту.
До наступления этого момента поток блокируется.
Вызов wait() внутри критического интервала приводит к тому, что
текущий поток уступает монопольное право на критический интер-
вал и приостанавливается до тех пор, пока из какого-либо друго-
го потока не будет сделан вызов notify() или notifyAll(). Хоро-
шей иллюстрацией использования средств синхронизации потоков
является упоминавшаяся выше программа Марка Тиллотсона.
class my_buffer
{
Object [] vec = new Object [8] ;
int ip = 0 ;
int ep = 0 ;
synchronized void insert (Object item)
{
do
{
if (ip-ep < 8)
{ vec [(ip++) & 7] = item ;
if (ip-ep == 1) notify () ; // Уведомить, если буфер был пуст
return ;
}
try wait () ; catch (InterruptedException e) ;
} while (true) ;
}
synchronized Object extract ()
{
do
{
if (ip > ep)
{ Object result = vec [(ep++) & 7] ;
if (ip-ep == 7) notify(); // Уведомить, если буфер был полон
return result ;
}
try wait () ; catch (InterruptedException e) ;
} while (true) ;
}
}
class my_producer extends Thread
{
int items_to_do ;
my_buffer the_buffer ;
my_producer (my_buffer buf, int count)
{ super() ;
the_buffer = buf ;
items_to_do = count ;
}
public void run ()
{
while (items_to_do > 0)
{ System.out.println ("producer to_do = " + items_to_do) ;
Integer item = new Integer (items_to_do*items_to_do) ;
the_buffer.insert (item) ;
items_to_do-- ;
}
System.out.println ("Производитель заканчивает работу") ;
}
}
class my_consumer extends Thread
{
int items_to_do ;
my_buffer the_buffer ;
my_consumer (my_buffer buf, int count)
{ super() ;
the_buffer = buf ;
items_to_do = count ;
}
public void run ()
{
while (items_to_do > 0)
{ System.out.println ("consumer to_do = " + items_to_do) ;
Object item = the_buffer.extract () ;
System.out.println ("consumer got " + item) ;
items_to_do-- ;
}
System.out.println ("Потребитель заканчивает работу") ;
synchronized (this){
notify () ; // Посылаем уведомление о завершении работы
// (см. con.wait() в main())
}
}
}
public class threaded3
{
public static void main (String [] args) throws InterruptedException
{
my_buffer the_buffer = new my_buffer () ;
my_producer prod = new my_producer (the_buffer, 40) ;
my_consumer con = new my_consumer (the_buffer, 40) ;
Thread.currentThread().setPriority (5) ;
prod.setPriority (4) ; // Производитель получает более высокий приоритет
con.setPriority (3) ; // по сравнению с потребителем
prod.start() ;
con.start() ;
synchronized (con)
{
con.wait() ; // Ждем уведомления от производителя об окончании
// его работы
}
System.out.println ("Производитель и потребитель закончили работу") ;
}
}
Приведенная программа написана в очень хорошем, понятном стиле.
Мы прокомментируем лишь один момент. В методах insert() и
extract() класса my_buffer вызов wait() содержится внутри бес-
конечного цикла. Дело в том, что вызов notify() относится к об-
®екту в целом. "Разбуженный" об®ект должен проанализировать
свое состояние и решить, что делать дальше. Так, если "заснул"
метод insert(), то после возобновления работы необходимо прове-
рить, что буфер уже не полон и добавление нового элемента стало
возможным. Если это не так, метод insert() заснет вновь.
* 4.2. Технология Java
4.2.1. Технологический цикл обработки Java-программ
В принципе, технологический цикл подготовки, трансляции, редак-
тирования внешних связей, тестирования, отладки и выполнения
Java-программ тот же, что и для других интерпретируемых языков
программирования, но с одним существенным отличием - при редак-
тировании внешних связей требуемые компоненты могут доставлять-
ся по сети (рис. xxx).
Рис. xxx. Технологический цикл Java-программы
Важно отметить, однако, что Java-программы могут представать
как бы в двух ипостасях - как самостоятельное приложение и как
аплет, то есть совокупность об®ектов, выполняющихся в среде
WWW-навигатора.
С точки зрения программиста, аплет и приложение отличаются в
первую очередь точками входа и жизненным циклом.
Приложение в качестве точки входа имеет метод
public static void main (String args[]);
этот метод должен быть определен в том public-классе, который содержится в
файле, выполняемом виртуальной Java-машиной.
В параметр args передается массив строк - параметров командной строки.
Пример: программа, печатающая свои аргументы
public class myTop {
public static void main (String args[]){
int argc = args.length;
for (int i = 0; i < argc; i++)
System.out.println (argc[i]);
}
}
Аплет выполняется в контексте навигатора и его жизненный цикл
определяется следующими методами класса Applet:
public void init () вызывается навигатором при загрузка аплета;
public void start (); вызывается навигатором при показе страницы;
public void stop (); вызывается навигатором, когда тот уходит с Web-страницы;
public void destroy (); этот метод предназначен для освобожденя
ресурсов; аналог деструктора, но не вызывается автоматически; всегда
Читать дальше