Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

GrandM-Patterns_in_Java

.pdf
Скачиваний:
97
Добавлен:
14.03.2016
Размер:
8.88 Mб
Скачать

Single Threaded Execution 8 449

ПРИМЕНЕНИЕ В JAVA API

сМногие методы класса j ava . u t i l . Vector являются синхронизированными целью обеспечения последовательного доступа к внутренним структурам дан­

ных объектов Vector.

ПРИМЕР КОДА

Приведем код, реализуюший проект для сенсора движения транспорта, рас­ смотренный в разделе «(Контекст». Сначала приводится код для класса Traf­ ficSensor. Экземпляры класса Traf ficSensor связаны с сенсором движения транспорта, который фиксирует прохождение машиной некоторого места на полосе движения. В этот момент вызывается метод detect экземпляра, отве­ чаюший за оповешение всех заинтересованных объектов о прохождении транс­ портного средства.

public class TrafficSensor implements Runnable ( private TrafficObserver oЬserver;

/ * *

*Constructor

*@param observer

*

Объект, предназначенный для оповещения о том, что сенсор

*

движения транспорта, связанный с этим объектом,

*обнаруживает проходящую машину.

* /

 

 

public TrafficSensor (TrafficObserver observer) (

 

this . observer = observer ;

 

 

new Thread (this) . start ( ) ;

 

 

/ / cons tructor (TrafficObserver)

 

/ * *

 

 

*

Общая логика для потока этого объекта .

* /

 

 

public void run ()

(

 

}

monitorSensor ()

;

 

/ / run ( )

 

 

 

 

 

/ /

Этот метод вызывает метод detect

Объекта, когда

/ /

связанный с ним сенсор движения транспорта фиксирует

/ /

проходящую машину .

 

private native void monitorSensor ()

;

/ /

Этот метод вызывается методом monitorSensor ,

/ /

чтобы сообшить о прохождении транспортного средства

/ /

наблюдателю этого объекта .

 

450

Глава 9. Шаблоны проектирования для конкурирующих операций

private void detect ( ) { observer . vehiclePassed () ;

} / / detect ( )

/ * *

*Классы должны реализовывать этот интерфейс,

*чтобы объе т TrafficSensor мог сообщить им о прохождении

*машин .

* /

public interface TrafficObserver {

/ * *

* Вызывается тогда , oгдa TrafficSensor фи сирует

*проходящее транспортное средство .

*/

public void vehiclePassed () ;

// in terface TrafficObserver

//class TrafficSensor

Следующий класс - TrafficTransmitter. Экземпляры класса TrafficTrans­ mitter отвечают за передачу значения, определяющего количество машин, про­ шедших через данное место дороги за одну минуту.

public class TrafficTransmitter implements Runnable

private

TrafficSensorController controller ;

private

Thread myThread;

/

*

*

 

 

const ructor .

 

*

@param cont roller От TrafficSensorControl ler этот объе т

 

*

будет получать значение счетчи а оличества прошедших

 

*

машин .

 

* /

 

public

TrafficTransmitter (TrafficSensorController= controller) { this . controller controller;

myThread = new Thread (this) ; myThread . start ( ) ;

// constructor (TrafficSensorContro ller)

/* *

*

Передает значение счетчи а оличества машин , прошедших

*

за одну минуту .

* /

 

Single Threaded Execution 8 451

public void run () while (true) {

try (

mуТhrеаd . slеер (БО*lООО) ;

transmit (controller . getAndClearCount (» ; catch (InterruptedException е) (

// t ry

// while

// run ( )

// Передает значение счетчика количества машин . private native void transmit (int count) ;

//class Tra fficTransmi tter

инаконец, приведем класс TrafficSensorController. Экземпляры класса

TrafficSensorControl ler хранят текущее общее количество машин, про­ шедших мимо сенсоров движения транспорта, связанных с экземпляром. Заме­ тим, что его методы реализованы как синхронизированные.

public class TrafficSensorController

implements TrafficSensor . TrafficObserver

private int vehicleCount = О ;

/ * *

* Этот метод вызывается в том случае , когда сенсор движения

*транспорта фиксирует прохождение машины . Он увеличивает

*значение счетчика машин на единицу .

* /

public synchronized void vehiclePassed () vehicleCount++ ;

}/ / vehiclePassed ( )

/ * * * Сбрасывает счетчик машин в нуль .

* @ return Возвращает последнее значение счетчика .

* /

public synchronized int getAndClearCount () int count = vehicleCount;

vehicleCount - О ; return count;

/ / getAndClearCount ( )

/ / c l a s s TrafficSensorController

452

Глава 9.

Шаблоны проектирования для конкурирующих операций

ШАБЛОНЫ ПРОЕКТИРОВАНИЯ, СВЯЗАННЫЕ

С ШАБЛОНОМ SINGLE THREADED EXECUTION

Почти все другие шаблоны ДЛЯ конкурирующих операций используют шаблон

Siпglе Threaded Ехесutiоп.

S ys tem . identi t yHas hCode,

СИНОПСИС

Должна выполняться операция, требующая однопоточного доступа к много­ численным объектам. Чтобы сэкономить время и упростить задачу, при помо­ ши шаблона Lock Object создается дополнительный объект блокировки, кото­ рый заменяет все объекты, непосредственно участвующие в операции. Объект,

который предназначен только для того, чтобы быть заблокированным, называ­ ется объектом блокировки.

КОНТЕКСТ

Предположим, создается программадля игры, которая будет происходить в реаль­ ном времени и в которой будет участвовать множество игроков. Каждый игрок сможет инициировать операции, которые влияют на состояние мноroчислен­

ных игровых объектов. Чтобы гарантировать надлежащее изменение состояния каждого объекта, при разработке необходимо применить шаблон Siпglе Threaded

Ехесutiоп.

ТОВЧтобы игра правильно функционировала, изменения некоторых игровых объек­

должны происходить одновременно и не влиять на действия другоro игрока. Для этого нужно гарантировать следующее: поток, отвечающий за изменение состояния объектов, должен иметь монопольный доступ к этим объектам.

С этой целью можно потребовать от потоков блокировать каждый объект перед

тем, как они пытаются изменить его состояние. Рещение такого рода связано с рядом сложностей.

1. Блокирование многочисленных объектов требует дополнительных ресурсов компьютера, но это наименьшая из проблем.

2.Необходимо быть уверенным, что при попытке блокировать несколько объ­ ектов потоки не окажутся взаимно блокированными. Не должна возник­ нугь ситуация, при которой один поток блокирует объект, который уже за­ блокирован другим потоком, и этот объект не будет освобожден до тех пор, пока второй поток не сможет получить доступ к друroму объекту, заблоки­ рованному первым потоком.

Подобных взаимных блокировок можно избежать, если использовать щаб­ лон Static Lосkiпg Order, описанный в книге [Grand2001). В таком случае за­ ставляют потоки сначала сортировать объекты в соответствии с тем значе­ нием, которое возвращает для них и затем блокируют их в этом порядке.

454 Глава 9. Шаблоны проектирования ,[!,Ля конкурирующих операций

Очевидно, что попытка избежать взаимной блокировки усложняет Про­ грамму и требует дополнительных затрат.

3.Блокирование коллекции объектов требует больших трудозатрат при коди­ ровании на языке Java. Чтобы заблокировать произвольное количество объ­

ектов, необходимо выполнять манипуляции внутри конструктора s ynchro­ nized рекурсивного метода. Рассмотрим пример:

public class ObjectLocker (

/* *

* Этот метод блокирует все объекты данного массива

*

и передает массив

объекту Obj ectMan ipu l at ionI F .

*

 

 

*

@param obj s

 

*

Массив объектов , на который воздействует

*

данньrn объект

Obj ectMan ipulation I F .

*@param ор

*Объект Obj ectManipulation I F .

*/

public void doIt (Object []

objs , ObjectмanipulationIF ор) (

doItнelper (obj s ,

ор , obj s . lenqth-l) ;

} / /

do I t ( Obj ect [ ] ,

Obj ectManipulationIF)

private void doItRelper (Object [] objs ,

 

 

 

ObjectмanipulationIF ор ,

 

 

 

int ndx)

synchronized

(objs [ndx] ) {

 

if (ndx=O)

(

 

 

 

op . doObject (obj s) ;

 

else (

 

 

 

 

doItнelper (objs,

ор , ndx-l) ;

 

/ / i f

 

 

 

 

/ / synchron ized

 

 

//

doI tHelpe r (Object [ ] ,

Obj ectMan ipu lationIF, i n t )

// c l a s s Obj ectLocker

Очевидно, длячто желательно найти альтернативное решение этой проблемы:

обеспечить потока монопольный доступ к многочисленным игровым объ­

ектам. При этом решение не должно быть таким сложным и требующим боль­ ших затрат, как решение, предусматривающее блокировку многочисленнЫХ

объектов.

Проблему можно УПРОСТИТЬ, если заставить только один поток в какой-то мо­

мент времени получать доступ к любым объектам игры. Это ограничение может

Lock Object 8 .455

вынудить потоки ждать, делать. Однако меньшая

хотя в противном случае они не должны были бы это сложность и меньшие затраты того стоят.

Для реализации этой упрошенной стратегии создают один новый объект, кото­

рый будет использоваться только как объект блокировки. Правило таково, что ни один поток не сможет каким-либо образом повлиять на состояние игрового

объекта до тех пор, пока он не заблокирует объект блокировки.

МОТИВЫ

©Чтобы обеспечить правильность выполнения операций, должны выпол­

няться операции, требующие монопольного доступа к объектам, на которые эти операции воздействуют.

©Необходимость блокировки произвольного набора объектов с целью вы­

полнения некоторой операции усложняет эту операцию и требует дополни­

тельных затрат на нее.

©Вполне допустимо, чтобы перед тем, как начаться, операция иногда ожида­ ла завершения другой операции. При этом требования к производительно­ сти являются настолько умеренными, что иногда приходится ждать завер­

шения другой операции даже в том случае, когда в этом нет необходимости.

РЕШЕНИЕ

Нужно организовать потоки таким образом, чтобы они получали монопольный доступ к набору объектов, выполняя блокировку синхронизации объекта, кото­ рый существует только для этой цели. Взаимодействие, предусматривающее использование объекта блокировки, описывается шаблоном, представленным

на рис. 9.6.

1: ---+

doItO {concurrency..guarded:tockObject}

 

IL....

 

 

 

-

 

I

 

-----(

 

---+

-

 

! tockObject:Object !

1.1*:operationO

 

 

 

 

 

--I

.

Рис. 9.6. Взаимодействие классов, использующих объект блокировки

Как показано на схеме, вызывается метод dOl t объекта Foo. Метод dol t начи­ нает с блокировки объекта lockObj ect. После этого он выполняет операции

с другими объектами, а затем снимает блокировку объекта блокировки.

Объект блокировки может быть внедрен в некоторую структуру классов различ­

ными способами. Например, на рис. 9.7 изображен абстрактный класс, кото­

рый имеет статический метод getLockObj ect. Подклассы этого абстрактного

456 Глава 9. Шаблоны проектирования дпя конкурирующих операций

киласса вызывают метод getLockObj ect с целью получения объекта блокиров_

для синхронизированных операций. Такой способ управления объектом

блокировки хорошо работает в тех случаях, когда нужно, чтобы все экземпляры

одного или нескольких классов совместно использовали один и тот же объект

блокировки.

Abstract5uperclass 9CtLQ!;kQbiect() ;Q12iC!;t

I

I

 

ConcreteClassl I

I

I ConcreteClassl

l

. .

 

Рис. 9.7.

Объект блокировки

 

Другие способы управления объектами блокировки рассматриваются в разделе

« Реализация» . Поскольку существует множество приемлемых способов управ­ ления доступом к объектам блокирqвки, они на самом деле не являются частью данного шаблона. Этот шаблон посвящен использованию объекта блокировки,

а не управлению доступом к нему.

РЕАЛИЗАЦИЯ

Существует множество различных способов управления доступом к объекту

блокировки. Основным доводом в пользу того или иного способа управления объектом блокировки является разнообразие тех объектов, которые будут ис­

пользовать объект блокировки, а также ответ на вопрос, будет нужен один или

несколько объектов блокировки.

Пример управления объектом блокировки, представленный на рис. 9.7, преду­

сматривает использование классов, которые получат доступ к объекту блоки­

ровки через наследуемый ими статический метод. Такая схема хорошо работает

в тех случаях, когда нужно заблокировать все экземпляры класса или классов

для выполнения некоторой операции.

Если почти все объекты, блокируемые для выполнения операции, не являются

экземплярами одного и того же класса, их, как правило, можно сгруппировать

по другому принципу. В таких случаях способ управления объектом блокиров­

ки обычно определяется на основе схемы объектов, участвующих в операциЯХ,

для которых предназначен объект блокировки. В качестве примера рассмотриМ

структуру объектов, имеющую вид дерева (рис. 9.8).

Объекты, организованные в виде дерева, являются экземплярами разных клас­

сов. Но все объекты имеют метод getLockObj ect, который возвращает объект

блокировки, используемый операциями, выполняемыми с участием этих объ­ ектов. Метод getLockObj ect реализован в этих объектах таким образом, что,

Lock Object 8 457

 

 

 

«interface»

 

 

 

 

 

 

 

 

 

 

LockIF

 

 

 

 

getLockObjectO :Object

 

 

 

 

*

 

 

 

 

 

,

 

 

 

:

 

,

 

 

I

 

:

 

 

,- - - - - - - - - - - - - - - - - - - - , - - - - - - - - - - - - - - - - - - - I,

Foo

 

 

Ваг

 

 

BLetsh

I

I

 

 

J

:Foo

L

 

 

lfQQ

I

I

lВ!r

I

I

 

I

 

'"

 

I

I

 

I

 

 

 

Рис. 9.8. Объекты дерева блокировки

если объекты не являются корнями дерева, их метод getLockOb j ect вызывает метод getLockObj ect родительского объекта. Если объект является корнем

дерева, он предоставляет реальный объект блокировки.

Если операция нуждается в монопольном доступе к любому из объектов, обра­ зующих дерево, она произвольным образом выбирает один из объектов, доступ к которому должен быть монопольным, и вызывает его метод getLockObj ect.

Метод getLockObj ect любого из объектов дерева возвращает один и тот же объект блокировки. Как только операция заблокирует объект блокировки, она получает монопольный доступ ко всем объектам дерева.

СЛЕДСТВИЯ

© Применяя шаблон Lock Object, можно гарантировать, что в какой-то мо­

мент времени доступ к набору объектов получает только один поток. Это реализуется достаточно просто и не требует больших издержек.

® Операция, использующая шаблон Lock Object, получает монопольный дос­ туп ко всем объектам, которые могут принимать участие в этой операции

или любой другой операции. Это означает, что, если две операции могли бы

выполняться параллельно, поскольку они не влияют на один и тот же объ­ ект, они не смогут этого сделать.

458

Глава 9. Шаблоны проектированиядля конкурирующих операций

ПРИМЕНЕНИЕ В JAVA API

Класс j ava . awt . Component имеет метод getTreeLock. Этот метод возвраЩа_

ет объект, который используется классом Component и всеми его подклассами

как объект блокировки. Метод getTreeLock каждого объекта Component воз­

вращает один и тот же объект блокировки.

Поwдклассыт класса Component используются дЛя создания GUI, основанных На

А (Abstract Windows Toolkit, пакет инструментальных средств дЛя абстракт­

ных OKOH1) и Swing.

ПРИМЕР КОДА

Пример кода дЛя этого шаблона включает некоторые классы, которые преду­

сматриваются сценарием использования игровых объектов, описанным в раз­ деле « Контекст» . Ниже приводится часть абстрактного суперкласса тех классов, которые применяются дЛя моделирования классов игры.

public abstract class AbstractGameObject {

private static final Object lockObject = new Object () i

/ * *

* True , если этот объект выделяется . * /

private boolean qlowinqi

public static final Object qetLockObject () { return lockObjecti

} / / getLockObj ect ( )

public boolean isGlowinq ()

}

return qlowinqi

/ / i sGlowing ( )

public void setGlowinq (boolean newValue) { qlowinq = newValue;

} / / setGlowing (boolean)

// class AbstractGameObj ect

IИногда говорят просто: «оконная библиотека Java.>. (Примеч. ред.)

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]