GrandM-Patterns_in_Java
.pdf438 • Глава 8. Поведенческие шаблоны проектирования
docElement = myDocument . getChild (docIndex)
;
docIndex += 1 ;
if (docElement instanceof Paragraph) return (Paragraph) docElement;
return null;
1 1 getNextParagraph ( )
} 1 1 class DocumentVisitor
Следующий листинг - это класс ReorgVis i tor, который должен «посещать»
абзацы документа и перемещать в отдельный документ те из них, которые от
носятся к заданному уровню структуры оглавления.
class ReorgVisitor extends DocumentVisitor private TocLevel [ ] levels ;
ReorgVisitor (Document document, int level) super (document) ;
this . levels = document . getTocLevels ( ) ; Paragraph р;
while « р = getNextParagraph (» ! = null)
}11 while
1 1 constructor ( Document)
1 1 class ReorgVisitor
впоследнем листинге описан класс TOCVis i tor. Класс TOCVisi tor отвечает
за создание оглавления. Он более глубоко проникает в CтpYJ<1YPy объектов доку мента, манипулируя при этом объектами Document, Paragraph и LineOfText.
Он использует объект Li neOfText, потому что элемент оглавления будет со
держать текст из первого объекта LineOfText из абзаца, которому соответству
ет элемент оглавления.
class TOCVisitor extends DocumentVisitor ( private Hashtable tocStyles = new Hashtable () ;
TOCVisitor (Document document) super (document) ;
TocLevel [ ] levels = document . getTocLevels ( ) ;
1 1 Помещаем стили в хэш-таблицу .
for (int i=O ; i < levels . length ; i++)
tocStyles . put (levels [i] . getStyle () , levels [i] ) ;
}1 / for
11 constructor (Document )
Visitor • 439
ТОС buildTOC () |
{ |
|
|||
ТОС toc = new ТОС ( ) ; |
|
||||
Paragraph р ; |
|
|
|||
while |
« р = getNextParagraph (» ! = null) |
||||
String styleN&me = p . getStyle () ; |
|||||
if |
(styleName ! = null) |
{ |
|||
|
|
TocLevel level ; |
|
||
|
|
level = |
(TocLevel) tocstyles . get (styleName) ; |
||
|
|
if (level ! = null) |
{ |
||
|
|
LineOfText firstLine = null ; |
|||
|
|
for |
(int i = О ; i < p . getChildCount () ; i++) { |
||
|
|
DocumentElementIF е = p . getChild (i) ; |
|||
|
|
if |
(е instanceof |
LineOfText) { |
|
|
|
|
firstLine = (LineOfText) е ; |
||
|
|
|
break; |
|
|
|
|
|
// |
i f |
|
|
|
) |
// |
for |
|
} |
|
// |
if |
|
|
// if |
|
|
|
||
|
|
|
|
/ / while
return toc;
// buildTOC ( )
//class TOCVisitor
ШАБЛОНЫ ПРОЕКТИРОВАНИЯ, СВЯЗАННЫЕ С ШАБЛОНОМ VISITOR
вIterator. Шаблон Iterator представляет собой альтернативу шаблону Visitor
том случае, когда структура объектов, по которой осушествляется навигация,
ЯВЛЯется линейной.
Little Language. Шаблон Visitor можно использовать в шаблоне Little Language дЛя реализации некоторой его части.
Composite. Шаблон Visitor часто используется вместе с такими структурами
объектов, которые организованы при помоши шаблона Composite.
'Л А В А
Шаблоныпроектирования
v
ДЛ Я конкурирующих операции
ngle Threaded Execution (Однопоточное выполнение) (443) )ck Object (Объект блокировки) (453)
uarded Suspension (Охраняемая приостановка) (460) !lking (Отмена) (468)
:heduler (ПЛанировщик) (473)
ad/Write Lock (Блокировка чтения/записи) (483) roducer-Consumer (Производитель-потребитель) (493) wo-Phase Tennination (Двухфазное завершение) (499) ouble Buffering (Двойная буферизация) (504) iупсhrопоus Processing (Асинхронная обработка) (521) Jture (Будущее) (534)
редставленные в данной главе шаблоны предназначены для координирова
rfя конкурирующих операций и должны решать главным образом проблемы
IYX разных типов,
Совместно используемые ресурсы. Если конкурирующие операции обраща ются к одним и тем же данным или другому общему ресурсу, они могут кон фликтовать друг с другом, если эти операции осушествляют доступ к ресур су в один и тот же момент. Чтобы обеспечить правильное выполнение таких операций, их нужно ограничить таким образом, чтобы доступ к обшему ре сурсу в какой-то момент получала только одна операция. Однако чрезмер
ное ограничение операций может привести к их взаимной блокировке и не возможности выполнения.
Шаблон Single Threaded Execution известен также под названием Critical Section.
СИНОПСИС
Обращение некоторых методов к данным или другим общим ресурсам может привести к ошибочным результатам, если имеют место конкурентные вызовы метода, желающие получить доступ к данным или другому ресурсу одновремен но. Для решения этой проблемы шаблон Single Threaded Execution препятствует
конкурентным вызовам метода, тем самым запрещая параллельное выполне ние этого метода.
КОНТЕКСТ
Предположим, что создается ПО дЛЯ системы, которая контролирует движение транспорта на главном шоссе. В стратегических точках, расположенных на шоссе, датчики фиксируют количество машин, прошедших в течение одной минуты. Датчики отправляют информацию на главный компьютер, который управляет электронными знаками, расположенными вблизи главных дорожных развязок. Эти знаки выводят сообщения для водителей, информируя их об ус ловиях на дорогах с тем, чтобы водители могли выбрать альтернативные мар шруты.
датчики измеряют поток машин, располагаясь на каждой полосе дороги. Все датчики присоединены к контроллеру, который суммирует количество машин,
прошедших данное место дороги в течение каждой минуты. Контроллер связан
с передатчиком, который посылает на главный компьютер все итоговые суммы,
представляющие количество машин, прошедших за одну минуту. На рис. 9. 1
представлена диаграмма классов, описывающая эти отношения.
Рассмотрим эти классы.
TrafficSensor. Каждый экземпляр этого класса соответствует физическому сенсор
Ному устройству. Каждый раз, когда машина проходит мимо физического сен
Сорного устройства, соответствующий экземпляр класса TrafficSensor вызы
вает метод vehiclePassed класса TrafficSensorController. Каждый объ
ект TrafficSensor выполняется в своем собственном потоке, что позволяет
Управлять входными данными от связанного с ним сенсора асинхронно по от Ношению к другим сенсорам.
TrafficTransmitter. Экземпляры этого класса отвечают за передачу данных о ко
Личестве машин, прошедших через некоторое место на дороге в течение одной
Минуты. Объект Tra f f i cTransmi tter получает величину, обозначающую
444 • Глава 9. Шаблоны проектирования для конкурирующих операций
количество машин, прошедших определенное место дороги, вызывая метод getAndClearCount соответствуюшего объекта Tra fficSensorController. Метод getAndClearCount объекта TrafficSensorController возвращает
число, обозначающее количество машин, прошедших мимо сенсора с момеНта предьщущего вызова метода getAndClearCount.
|
|
|
|
|
|
|
TrafficSensor |
||||
1..* |
|
|
|
|
|
1 , |
Оповещает о прохождении транспортного средства...... |
||||
|
TrafficSensorController |
|
|||
|
vehicleCount:int |
|
|||
|
vehicLePassed() |
|
|
|
|
|
getAn dCLearCount():int |
|
|||
|
1 |
|
|
|
|
|
|
|
|
|
|
1 |
Получает данные по учету движения транспорта .... |
||||
|
|
||||
|
TrafficTransmitter |
|
Рис. 9.1. Классы датчика движения транспорта
TrafficSensorController. Экземпляры класса TrafficSensor и класса TrafДЛЯ ficTransmitter вызывают методы класса TrafficSensorController
обновления , считывания и очистки переменной, содержащей количество ма шин, прошедших через некоторое место дороги.
Возможна ситуация, когда два объекта TrafficSensor вызывают метод vehi clePassed объекта TrafficSensorControl ler одновременно. Если оба вы
зова будут выполняться в одно и то же время, то это приведет к ошибке.
Предполагается, что каждое обращение к методу vehiclePassed должно увели
чивать значение счетчика машин на единицу. Однако, если в один и тот же мо
мент времени выполняются два обращения к методу vehicle Pa ssed, счетчик
машин увеличивается не на два, а на единицу. Опишем последовательность со
бытий для случая одновременного выполнения обоих обращений к методу.
1. Оба вызова одновременно считывают одно и то же значение vehicleCount.
2. Оба вызова добавляют единицу к одной и той же величине.
З. Оба вызова сохраняют одно и то же значение в vehicleCount.
Очевидно, что, допуская одновременное выполнение нескольких обращениЙ
к методу vehiclePa ssed, в результате получим число, которое меньше реаль
ного количества прошедших машин. Хотя небольшое занижение количества
машин не может представлять серьезной угрозы для этого приложения, суше-
ствует похожая проблема, которая является более серьезной .
Объект TrafficTransmitter периодически вызывает метод getAndClearCount
объекта Traff icSensorController. Метод getAndC learCount считывает зна-
446 |
• |
Глава 9. |
Шаблоны проектирования для конкурирующих операций |
Сколько угодно потоков MOryr обрашаться к охраняемым методам одного и тоГо
же объекта одновременно. Однако в какой-то момент только одному потоку
разрешено выполнение охраняемых методов объекта. Пока один поток ВЫПол няет охраняемые методы объекта, другие потоки ожидают до тех пор, пока по
ток не завершит выполнение этих методов. Затем дЛя выполнения следующих
охраняемых методов объекта будет выбран произвольным образом другой по
ток из числа ожидаюших. Такой механизм гарантирует однопоточное выполне
ние охраняемых методов объекта.
МОТИВЫ
©Класс содержит методы, которые обновляют или задают значения в пере менных экземпляра или переменных класса.
©Метод манипулирует внешними ресурсами, которые поддерживают ТОлько одну операцию в какой-то момент времени.
©Методы класса MOryr вызываться параллельно различными потоками.
©Не сушествует временного ограничения, которое требовало бы от метода
немедЛенного выполнения, как только его вызывают.
РЕШЕНИЕ
Гарантировать, чтобы операции, параллельное выполнение которых даст ошнеи
бочный результат, не выполнялись бы параллельно. С этой целью методы, ПОдЛежашие параллельному выполнению, делаются охраняемыми (рис. 9.3).
Resource
safeOpl safeOp2
unsafeOpl {concurrency-guarded} unsafeOp2 {concurrency-guarded}
Рис. 9.3. Шаблон Single Threaded Execution
Класс, изображенный на рис. 9.3, имеет методы двух видов. Первый вид - не |
|||||
охраняемые методы safeOp l , safeOp2 И Т.Д., которые MOryr безопасно "otf |
|||||
курентно выполняться. Второй вид - охраняемые методы unsafeOpl, unsafeOp2 |
|||||
и Т.Д. , которые не MOryr безопасно параллельно выполняться. Если различные |
|||||
потоки одновременно вызывают охраняемые методы объекта Resou rce |
, в ка |
||||
кой-т |
|
|
потоку разрешено выполнять так |
|
ме |
о момент времени только одному |
оЙ |
|
|||
тод. Остальные потоки |
ЖДУТ, |
пока этот поток не закончит свою работу. |
|
|
|
|
|
|
Single Threaded Execution 8 447
РЕАЛИЗАЦИЯ
в языке Java охраняемые методы реализуются посредством объявления их син хронизированными. Для вызова синхронизированного метода может потребо ваться больше времени, чем для вызова несинхронизированного. Рассмотрим диаграмму взаимодействия (рис. 9.4).
lа: fooO {concurrency-guarded} 1
lа.l: doItO {concurrency-guarded} 1
Рис. 9.4. Факторинг синхронизации
Синхронизированный метод класса А вызывает метод do 1t класса В. Метод dolt является синхронизированным. Если метод dol t вызывается только син
хронизирови анными методами класса А, то можно произвести оптимизацию
сделать метод dol t несинхронизированным. Он будет выполняться только
одним потоком в какой-то момент времени, поскольку он вызывается только
такими методами, выполнение которых предусматривает участие только одного потока в некоторый момент времени.
Такая оптимизация называется факторингом синхронизации. Факторинг син хронизации (synchronisation factoring) - это небезопасная оптимизация в том Смысле, что, если программа будут изменена так, что к методу dol t будут про Изводиться параллельные обращения, программа перестанет правильно функ
ционировать. Поэтому, если разработчик решает, что оптимизацию стоит про иВодить «(вручную» , он должен внести комментарии в диаграммы проекта
Исходный текст кода, чтобы предупредить программистов о выполненной оп l'Имизации.
СУществуют компиляторы и NM, которые выполняют подобную оптимизацию
аВтоматическиSun. : например, NM, применяющие технологию HotSpot фирмы
Существуют компиляторы, которые могут осуществлять факторинг син
Хронизации в определенных случаях. При использовании NM или компиля l'ора, который делает оптимизацию автоматически, лучше не делать подобную
Оптимизацию вручную.