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

GrandM-Patterns_in_Java

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

Readl'Nrite Lock 489

Очевидно, что методы класса Bid используют объект ReadWri teLock для со­ гласования параллельных обращений. Сначала (перед чтением или записью значений) они вызывают соответствующий метод блокировки. По завершении они вызывают метод done объекта ReadWri teLock С целью разблокировки.

Класс ReadWri teLock более сложен. По мере прочтения его листинга можно заметить, что он уделяет основное внимание двум моментам.

1. Тщательно отслеживает информацию о состоянии, используя при этом ме­ ханизм, который совместим со всеми потоками.

2.Обеспечивает выполнение всех предварительных условий, преЖде чем вы­

полнятся его методы блокировки.

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

public class ReadWriteLock {

private int waitingForReadLock = О ;

private int outstandingReadLocks = О ;

private ArrayList waitingForWriteLock = new ArrayList ( ) ;

private Thread writeLockedThread;

Объект ReadWr i teLock использует эти переменные экземпляра для отслежи­ вания потоков, которые запросили или получили запрос на блокировку чтения или записи. Для отслеживания потоков, которые ожидают блокировки записи, он использует список, на который ссылается переменная wai t ingForWri ­ teLock. Применение такого списка позволяет осуществлять блокировки запи­ си в том порядке, в котором выдавались соответствующие запросы.

Объект ReadWri teLock использует переменную wai ti ngForReadLock дЛя подсчета количества потоков, ожидающих блокировки чтения. Простой под­ счет является вполне достаточным, поскольку все потоки, ожидающие блоки­ ровки чтения, получат разрешение на них одновременно. Это значит, что не нужно отслеживать порядок запрашивания потоками блокировки чтения.

Объект ReadWri teLock использует переменную outstandingReadLocks дЛя подсчета количества блокировок чтения, которые были инициированы, но еще не освоБОЖдены соответствующими потоками, для которых они предназначались.

Объект ReadWriteLock использует переменную writeLockedThread для ССЬUIКИ на поток, у которого в данный момент блокирована запись. Если в данный мо­

мент времени ни у одного потока не блокирована запись, то переменная wri teLockedThread содержит null. Имея переменную, ССЬUIающуюся на по­ ток, у которого была блокирована запись, объект ReadWri teLock знает, был ли поток активизирован с целью блокирования записи или по другой причине.

Метод readLock объекта ReadWriteLock действует следующим образом. Он

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

490

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

нет невыполненной блокировки записи. Блокировка чтения состоит в том, ЧТо

значение в переменной outstandingReadLocks увеличивается на единицу.

synchronized public void readLock ( ) throws InterruptedException if (writeLockedТhread != null) {

waitingForReadLock++ ; != while (writeLockedThread; null)

wait ( )

} / / while waitingForReadLock- ;

/ / i f outstandingReadLocks++ ;

/ / readLock ( )

Дальше - листинг метода writeLock. Как можно заметить, этот листинг боль­ ше, чем листинг метода readLock. Это объясняется тем, что wri teLock управ­

ляет потоками и структурой данных. Код начинается с того, что проверяется

случай отсутствия невыполненных блокировок. Если нет невыполненных бло­ кировок, немедленно осуществляется блокировка записи. В противном случае текуший поток добавляется в список, который рассматривается методом done как очередь. Текущий поток ожидает до тех пор, пока метод done не разрешит ему блокировку записи. Затем метод wri teLock завершается, удаляя текущий поток из списка потоков, ожидающих блокировку записи.

public void writeLock () throws InterruptedException Thread thisThreadi

synchronized (this) {

if ( writeLockedThread--null

&& outstandingReadLocks==O) writeLockedТhread = Thread. currentThread ( ) ; return ;

/ / i f

thisThread = Thread . currentThread ( ) ; waitingForWriteLock . add (thisThread) ;

// s ynchroni zed ( this)

synchronized (thisThread)!- {

while (thisThread writeLockedThread) thisThread . wait ( ) ;

}/ / whi le

// synchronized ( thi sThread)

synchronized (this) {

waitingForWriteLock . remove (thisThread) ; } / / synchroni zed ( this)

/ / wri teLock

Read/Write lock 491

Метод done завершает листинг классаД1IЯ ReadWriteLock. Потоки вызывают ме­ тод done объекта ReadWri teLock снятия блокировки, ранее произведен­ ной объектом ReadWriteLock. Метод done рассматривает три случая.

1. Существуют невыполненные блокировки чтения, наличие которых предпола­ гает, что нет невыполненных блокировок записи. Он снимает блокировку чтения посредством уменьшения на единицу переменной outstand­ i ngReadLock. Если больше нет невыполненных блокировок чтения и су­ Дшествуют1IЯ потоки, ожидающие блокировки записи, то блокируется запись

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

2. Существует невыполненная блокировка записи. Метод done заставляет теку­ щий поток снять блокировку записи. Если имеются какие-либо потоки, ожидающие блокировки чтения, он инициирует блокировки чтения для всех этих потоков. Если нет невыполненнblX блокировок чтения и есть пото­ ки, ожидающие блокировки записи, этот метод блокирует запись того потока, который ожидает дольше всех, заставляя переменную wri teLockedThread ссылаться на этот, а не на текущий поток.

З. Нет невыполненных блокировок. Если нет невыполненных блокировок, это значит, что метод done был вызван ошибочно, поэтому он генерирует исключение I l legalStateException.

synchronized public void done ( )

if (outstandingReadLocks > О )

outstandingReadLocks- i

if ( outstandingReadLocks==O

" waitingForWriteLock . size ( » O) writeLockedThread=

(Тhread) waitingForWriteLock . get (O) writeLockedThreadif . notifyAll ( ) i

//

}else if (Thread . currentThread ( )

==writeLockedThread) {

if ( outstandingReadLocks==O

" waitingForWriteLock . size ( » O ) writeLockedThread=

(Thread) waitingForWriteLock . get (O)

writeLockedТhread. notifyAll ( ) i else . {

writeLockedТhread - null i

if

(waitingForReadLock > О )

 

notifyAll () i

/ /

i f

492

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

else (

String msg = "Thread does not have lock" ;

throw new IllegalStateException (msg) ;

/ / i f

// done ( )

// class ReadWriteLock

Отметим еще одну последнюю деталь в отношении метода done: вместо меТода noti fy он использует метод noti fyAll . Если ему нужно разрешить блокиров­

ку чтения, он вызывает метод noti fyA11 объекта ReadWri teLock, чтобы по­

зволить продолжить выполнениедля всем потокам, ожидающим блокировки чте­

ния. Когда он осуществляет некоторого потока блокировку записи, он вы­

зывает метод noti fyAll этого потока. Вызов метода noti fy будет работать

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

кировки синхронизированного потока с целью блокировки записи, использо­

вание метода noti fy может привести к активизации неправильного потока.

Применение метода noti fyAl l гарантирует активизацию нужного потока.

Сушествует более мошная версия класса ReadWri teLock,wwwкоторая является ча­ стью ПО ClickBlocks, которое можно найти на сайте .cIickbIocks1. .org. Этот класс находится в пакете под названием org . c1ickblocks . uti

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

С ШАБЛОНОМ READ/WRITE LOCK

Siпglе Threaded Ехесutiоп. Шаблон Single Threaded Execution - это хорошая

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

Scheduler. Шаблон ReadjWrite Lock - это специальная форма шаблона Scheduler.

Диспетчеризация претензиii, полученных
из очереди

СИНОПСИС

Координирует асинхронное производство и использование информации или объектов.

КОНТЕКСТ

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

Любое количество клиентов может в любое время предъявить претензии. На де­

журстве обычно находится множество диспетчеров. Если кто-либо из них не

занят, система немедленно передаст претензии одному из них. В противном

случае претензии будут помещены в очередь, пока их не передадут на просмотр диспетчеру (рис. 9.24).

l

0..* CLient

Претензии помещаются в очередь­

ДJlя диспетчеризации

1 ' "

 

 

 

 

 

I

Оиеие

 

 

push(:TrouЫеTicket)

 

 

puLLO:TroubLeTicket

 

 

 

sizeO:int

 

 

 

очередьт 1

 

 

 

 

I Dispatcher I

диспетчер 0..*

Рис. 9.24. Классы обработки претензий

Экземпляры класса C l ient отвечают за получение форм с претензиями, запол­ вlieHHbIX пользователями и помещенных в объект Queue. Претензии остаются объекте Queue до тех пор, пока объект Dispatcher не достанет их из объекта

Queue.

Класс Dispatcher отвечает за получение претензий диспетчером и затем на­ правляет их по адресу назначения, выбранному диспетчером. Когда экземпляр

класса Di spatcher не передает претензии или не напраWIяет их по назначению,

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

он вызывает метод pull объекта Queue с целью получения других претензиНйе.

Если в объекте Queue нет претензий, метод pull ожидает до тех пор, пока появятся претензии, которые он должен возвратить (рис. 9.25).

Рис. 9.25. Взаимодействие процесса обработки претензий

МОТИВЫ

©Объекты создаются или получаются асинхронно по отношению к их ис­

пользованию или потреблению.

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

РЕШЕНИЕ

Представленная на рис. 9.26 диаграмма описывает классы в рамках шаблона

Producer-Consumer.

Помещает в очередь

0..*

I Producer I

создаваемые объекты'"

 

 

1

1

 

Queue

 

push(:Object)

 

рuЩ):ОЬjеct

 

size():int

 

 

очередьт 1

 

 

Consumer

 

потребиI тель

 

 

0..-

Использует наХОДllщиеся в очереди

 

 

объекты

 

J

Рис. 9.26. Клоссы шаблона Producer-Consumer

Producer-Consumeг 495

Опишем роли, которые MOryr исполняться этими классами.

Producer. ЭкземruIЯРЫ классов в этой роли предоставляют объекты, использую­ щиеся объектами Consumer. ЭкземruIЯРЫ класса Producer создают объекты асинхронно по отношению к тем потокам, которые их используют. Это значит, что иногда объект Producer может создать объект в тот момент, когда все объ­ екты Consumerждутзаняты, обработкой других объектов. Экземпляры классов Producer не пока объект Consumer станет доступным, вместо этого они помещают созданные ими объекты в очередь и продолжают свою работу.

Queue. ЭкземruIЯРЫ классов в этой роли служат в качестве буфера для объектов, созданных экземплярами классов Producer. Экземпляры классов Producer помещают созданные ими объекты в экземпляр класса Queue. Объекты остают­ ся там до тех пор, пока объект Consumer не достанет их из объекта Queue.

Consumer. Экземпляры классов Consumer используют объекты, созданные объ­ ектами Producer. Они получают используемые ими объекты от объекта Queue. Если тот пуст, объект consume r, желающий получить от него объект, ожидает до тех пор, пока объект Producer не поместит объект в объект Queue.

Представленная на рис. 9.27 диаграмма взаимодействия демонстрирует взаимо­ действия между объектами, принимающими участие в шаблоне Producer-Con­ sumer.

:Producer

д: push(:Object) 1

....- ..1-__...,

В: pullO {сопсuпепсу-guагdеd I q.sizeO>O} r

,.с:::===::::::;,

:(опsumег

Рис. 9.27. Взаимодействие в шаблоне Producer-Consumeг

РЕАЛИЗАЦИЯ

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

ного размера, а поток производителя намеревается поместить в очередь еще

один объект. Тогда управление очередью обычно осуществляется при помощи шаблона Guarded Suspension, который вынуждает экземпляр класса Producer

496

Глава 9.

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

Ждать, пока экземпляр класса Consumer не заберет объект из очереди. Если для

объекта, который экземпляр класса Producer хочет поместить в очередь, есть

место, то ему разрешается закончить выполнение этой операции и продолжить

свою дальнейшую работу.

СЛЕДСТВИЯ

© Объекты Produce r могут поставлять производимые ими объекты объекту

Queue, не дожидаясь объекта Consumer.

Если в объекте Queue находятся объекты, объекты Consumer могут, не ожи­ дая, достать объект из очереди. Однако если очередь пуста, а объект Consumer вызывает метод pul l объекта Queue, то метод pull не выполня­ ется до тех пор, пока объект Producer не поместит объект в очередь.

ПРИМЕНЕНИЕ В JAVA ауаAPI. ауа .

Ядро Java API содержит классы j io . PipedlnputSt ream и j io . Pi­ pedOutputStream. Вместе они реализуют вариант шаблона Producer-Consumer, который называется шаблоном Pipe. Шаблон Pipe содержит только один объ­ ект Producer и только один объект Consumer. Этот шаблон обычно ссылается на объект Producer как источник данных и на объект Consumer как на прием­ ник данныхауа. .

Классы j io . PipedlnputStream и j ava . io . PipedOutputStream совмест­ но играют роль класса Queue. Они позволяют одному потоку записывать байто­ вый поток в другой поток. Потоки выполняют свои операции записи и чтения

асинхронно по отношению друг к другу, если используемый ими внутренний

буфер не пуст или не заполнен до предела.

ПРИМЕР КОдА

Следующие листинги содержат код, который реализует проект, рассмотренный

в разделе «Контекст». Первые два листинга - это скелетные коды для классов

Cl ient и Di spatcher.

public class Client implements Runnable { private Queue myQueue ;

public Client (Queue= myQueue) this .myQueue myQueue ;

/ / con s t ruc tor ( Queu e )

public void run ( ) {

Producer-Consumer _ 497

TroubleTicket tkt = null ;

myQueue . push (tkt) ;

// run ( )

// class C l ient

public class Dispatcher implements Runnable ( private Queue myQueue ;

public Dispatcher (Queue myQueue) this . myQueue = myQueue ;

}/ / constructor (Queue )

public void run ( ) (

TroubleTicket tkt = myQueue . pull ( ) ;

// run ( )

// class Di spatche r

Последний листинг содержит класс Queue.

public class Queue (

private ArrayList data = new ArrayList () ;

/* *

* Помещаем объе кт в конец очереди .

* @pa ram obj Объект , помещаемый в конец очереди .

* /

 

 

 

synchronized public void push (TroubleTicket tkt)

(

 

data . add (tkt) ;

 

 

 

notify ( ) ;

 

 

 

/ / push (TroubleTicket )

 

 

/ * *

 

 

 

*

Получаем TroubleTicke t , находящийся в

начале

очереди .

*

Если

очередь пуста , ждем, пока в ней

не появитс я объект .

* /

 

 

 

synchronized public TroubleTicket pull ()

(

 

 

while

(data . size ( ) == О) {

 

 

try { wait ( ) ;

} catch (InterruptedException е) {

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

}

/ / try

= (TroubleT1Cket) data . qet (O) ;

//

whi l e

TroubleTicket tkt

data . remove (O) ; return tkt ;

// pul l ( )

/* *

* Возвращаем количество претензий, находящихся * в этой очереди .

* /

public int size ( ) {

return data . size ( ) ;

} / / s i ze ( )

/ / class Queue

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

С ШАБЛОНОМ PRODUCER·CONSUMER

Guarded Suspension. Шаблон Producer-Consumer использует шаблон Guarded Suspension дЛЯ управления такой ситуацией, когда объект Consumer ожидает получения объекта из пустой очереди.

Pipe. Шаблон Pipe - это специальный случай шаблона Producer-Consumer, ко­ торый включает только один объект Producer и только один объект Consumer. Обычно шаблон Pipe ссылается на объект Producer как на источник данных и на объект Consumer как на приемник данных.

Scheduler. Шаблон Producer-Consumer может рассматриваться как специальная

форма шаблона Scheduler, политика планирования которого имеет две харак­

особенности:

основана на доступности ресурса;

назначает ресурс для потока, но не нуждается в повторном получении кон­

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

ресурс другому потоку.терные

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