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

GrandM-Patterns_in_Java

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

Asynchronous Processing - 529

в очередь. Объект очереди является экземпляром класса j ava . awt . EventQueue.

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

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

вуюшим получателям.

Объект EventQueue, используемый для передачи сообшений клавиатуры или мыши любому из компонент Swing или AWГ, можно получить при помоши

следуюшего кода:

import java . awt . Component; java . awt . EventQueue

Component с ; EventQueue evtQueue ;

evtQueue = c . getToolkit ( ) . getSystemEventQueue ( ) ;

ПРИМЕР КОДА

Пример кода для этого шаблона представляет собой клиентскую программу, асинхронно обрабатываюшую некоторые события пользовательского интер­ фейса. Этот подкласс класса JFrame из библиотеки Swing создает элемент ин­ терфейса00, показанный на рис. 9.34. В его выпадаюшем меню File содержится пункт It. Кроме того, в нижней части фрейма находится индикатор выпол­ нения.

Когда пользователь шелкает на пункт меню 00 It, индикатор отображает ход выполнения процесса на протяжении примерно00четырех секунд. В то время, пока индикатор выполнения изменяется, пункт It в меню File меняется на пункт Stop Ooint It. Если пользователь шелкнет на пункт меню Stop Doing It, то индикатор выполнения перестанет изменяться.

Независимо от того, остановился ли индикатор выполнения из-за того, что он достиг 100 %, или из-за того, что пользователь шелкнул на пункт меню Stop

Doing It, но, когда индикатор перестает показывать изменения, пункт меню

Stop Ooing It заменяется пунктом меню

00

It.

 

public class CancelFrame extends JFrame

 

JМenuItem menuHelpAbout =

new JМenuItem( ) ;

 

JМenu menuHelp =

new JМenu ( ) ;

 

 

 

JМenuItem menuFileDoIt = new JМenuItem() ;

 

JМenuItem menuFileStopDoingIt =

new JМenuItem ( ) ;

JМenuItem menuFileEx1t = new JМenuItem () ;

 

JМenu menuFile =

new JМenu ( ) ;

 

 

 

JМenuBar menuBarl

 

new JМenuBar ( ) ;

 

BorderLayout

borderLayoutl

=

new BorderLayout ()

i

 

к

 

 

 

530

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

JProgressBar myProgress = new JProgressBar ( ) ; Thread doltТhread;

public CancelFrame ( ) try (

jbInit ( ) ;

catch (Exception е) (

e . printStackTrace ( ) ;

// try

// cons tructor ( )

/* *

* Инициализирует содержимое этого фрейма .

*/

private void jbInit ( ) throws Exception (

this . setJМenuВar (menuBarl) ;

this . getContentPane ( ) . setLayout (borderLayoutl) ; this . setSize (new Dimension (400 , 300» ;

thi s . setTitle ("Cancel Example Application") ;

menuFile . setText ("File" ) ; menuFileDolt. setText ("Do It") ;

menuFileDolt . addActionListener (new ActionListener ( )

public void actionPerformed (ActionEvent ае)

(

fileDolt_ActionPerformed (ae) ;

 

} / / actionPerformed (ActionEvent )

 

} ) ;

 

menuFileStopDoinglt . setText ("Stop Doing It") ;

 

menuFileStopDoinglt . addActionListener (

 

new ActionListener ( ) (

 

public void actionPerformed (ActionEvent ае)

(

fileStopDoinglt_ActionPerformed (ae) ;

 

} / / actionPerformed (Act ionEvent )

 

} ) ;

menuFileExit . setText ("Exit" ) ;

menuFileExit . addActionListener (new Act1onL1stener ( ) public void actionPerformed (Act1onEvent ае) (

fileExit_ActionPerformed (ae) ;

» ;) / / act ionPe rformed (Act ionEvent )

menuHelp . setText ( "Help") ; menuНelpAbout . setText ("About") ;

Asynchronous Processing _

menuHelpAbout . addActionListener (new ActionListener ( ) public void actionPerformed (ActionEvent ае) {

}

helpAbout_ActionPerformed (ae) ;

}// actionPerformed (ActionEvent )

)

;

menuFile . add (menuFileDoIt) ; menuFile . add (menuFileExit) ; menuВarl . add (menuFile) ; menuHelp . add (menuHelpAbout) ; menuBarl . add (menuHelp) ;

this . getContentPane ( ) . add (myProgress , BorderLayout . SOUТH) ;

/ / j bInit ( )

/ * *

*Асинхронно устанавливает индикатор выполнения в ноль ,

*а затем постепенно100в течение нескольких секунд изменяет его показание до процентов .

* /

void fileDoIt_ActionPerformed (ActionEvent е ) { doItThread = new Thread ( )

public void run ( ) { try {

installStopItмenuItem ( ) ; myProgress . setмinimu m (O) ; myProgress . setмaximum (19) ; myProgress . setValue (О) ; myProgress . repaint ( ) ;

for (int i=O ; i<20 ; i++)

Thread . currentТhread ( ) . sleep (ЭОО) ; myProgress . setValue (i) ;

myProgress . repaint ( ) ;

/ / for

catch (InterruptedException е) {

finally {

 

lnstallDoItмenuItem ( ) ;

 

doItThread = null ;

 

/ / t ry

} ;

/ / run ( )

 

doItThread . start () ;

// f i leDoI t_Act ionPerformed (Act ionEven t >

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

private void installStopItмenuItem( ) { menuFile . remove (menuFileDoIt) ;

menuFile . insert (menuFileStopDoingIt , О) ; menuFile . repaint () ;

//i л st а l lStор ltМепu l tеm ( )

private void installDoItмenuItem ( ) menuFile . remove (menuFileStopDoingIt) ; menuFile . insert (menuFileDoIt, О) ; menuFile . repaint ( ) ;

/ / i л st а llDо l tМеЛ Ul tеm ( )

void fileStopDoingIt_ActionPerformed (ActionEvent е) if (doItТhread != null) {

doItThread. interrupt ( ) ;

} / / i f

// filеStорDОiлgl t_АсtiопРеrfоrmеd (Асt iолЕvелt )

void fileExit_ActionPerformed (ActionEvent е) System . exit (O) ;

} / / fil еЕХi t_Ас tiолРеrfоrmеd (АсtiолЕvеп t)

void helpAbout_ActionPerformed (ActionEvent е)

JPanel aboutPanel = new CancelFrame_AboutвoxPanell ( ) ; JOptionPane . showMessageDialog (this ,

aboutPanel , "About" ,

JOptionPane . PLAIN_МESSAGE) ;

}// hе lрАЬоut_АсtiолРеrfоrmеd;

/ / class Салсеl Frаmе

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

: ШАБЛОНОМ ASYNCHRONOUS PROCESSING

'hread Pool. Шаблон Thread Рооl (описанный в книге [Grand2001]) может ис­

ользоваться в реализации шаблона Asynchronous Processing.

'roducer-Consumer. Шаблон Producer-Consumer может использоваться в реали­

щии шаблона Asynchronous Processing.

cheduler. Шаблон Scheduler может использоваться в реализации шаблона

.synchronous Processing.

'acade. Реализация класса обработки запроса может применять классы, которые вляются продуктами использования шаблонов Thread Pool, Producer-Consumer

Asynchronous Processing 533

или Scheduler. Классы обработки запросов обычно выступают в роли фасада,

скрывающего эти детали от клиентских классов.

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

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

Active Object. Шаблон Active Object, рассмотренный в работе [SSRBOO], описы­

ваетспособ объединения щаблона Future с щаблоном Asynchronous Processing.

Этот шаблон известен также под названием Prornise.

СИНОПСИС

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

ляет скрыть от клиентов, синхронно или асинхронно выполняется вычисление.

Этот объект будет содержать метод чтения результатов вычислений. Метод

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

продолжает или выполняет вычисление, если оно является синхронным и еще не завершено.

КОНТЕКСТ

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

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

ваться как часть сервлета, который генерирует код HTML дЛЯ Web-страницы,

показывающей информацию о погоде наряду с новостями.

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

можно, несколько минут. Нужно спроектировать этот класс таким образом,

чтобы минимизировать влияние на клиентов этого класса.

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

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

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

щающий о наступлении события, то клиенты класса сильно усложнятся. Класс

клиента вынужден будет реализовать метод по получению новой информации

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

Лучше использовать структуру, показанную на рис. 9.37. Опишем классы, представленные на рис. 9.37.

Weather. Экземпляры этого класса инкапсулируют информацию о текущих по­

годных условиях определенной местности.

Client. Экземпляры класса, выступающего в этой роли, используют информа­

цию, находящуюся в объектах Wea ther.

WeatherRequester. Когда объект C l i ent хочет получить объект Weather, со­

держащий текущую информацию для данной местности, он просит объект

Future 535

I

*

«roLe)) CLient

1

Запрашивает информацию о погоде

WeatherRequester

getWeather(Location):WeatherFuture...

1

1

II

Соэдает'У

 

 

I

 

 

.!,

 

 

*

I

 

 

I

 

1

 

Проиэводит опрос, существует ли объект

WeatherFuture

 

1

1

Weather, и считывает объект Weather

checkO :booLean

 

 

* waitForWeatherO:Weather

 

 

Знает, существуетли объект, инкапсулирующий

1

 

 

 

запрошенную информацию

*...1

 

,, 0. . 1

1

 

 

ИСПОЛl,эует

 

 

 

 

 

Weather

 

Рис. 9.37. Классы, предоставляющие информацию о погоде

WeatherRequeste r запросить эту информацию от его имени. Объект C lient

делает это, вызывая метод getWeather объекта WeatherRequester. Метод getWeather не ждет, пока считается информация о погоде. Он заканчивает вы­ полнение немедленно. Возвращаемое им значение - это объект WeatherFu­

ture. Запрошенная информация о погоде считывается асинхронно.

WeatherFuture. Этот класс отвечает за то, чтобы позволить другим потокам со­

гласовывать свои действия с потоком, который считывает информацию о пого­

де. При вызове метод getWeather объекта WeatherReques ter инициирует

асинхронное считывание запрошенной информации о погоде и немедленно

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

Объект WeatherFutu re имеет метод check, который возвращает false, если он

вызывается до того, как была считана запрошенная информация и был создан объект Weathe r, содержащий эту информациЮ. После создания объекта Weather метод check возвращает true.

Объект WeatherFutu re содержит еще один метод - wai tFo rWeather. Если

этот метод вызывается до того, как был создан объект Weather, он ОЖИдает создания этого объекта. Как только объект Weather будет создан, метод waitForWeather немедленно возвращает этот объект Weather.

моти в ы

©Вычисление производится асинхронно по отношению к другим потокам,

использующим результат этого вычисления.

©Асинхронное вычисление может оповещать объект, использующий результат

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

Observer или генерируя для заинтересованноro объекта некоторое событие.

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

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

©Нужно инкапсулировать вычисление таким образом, чтобы его клиенты

ничего не знали о том, является ли это вычисление асинхронным или син­

хронным.

РЕШЕНИЕ

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

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

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

няется ли вычисление синхронно или асинхронно по отношению к их потоку.

На рис. 9.38 показаны классы в этом решении. Опишем роли, выполняемые

классами в шаблоне Future.

 

 

 

 

 

Запрашивает вычисление .

 

 

 

 

 

 

 

 

 

 

 

 

 

Requester

 

 

 

 

 

 

 

 

dolt(params):Future

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

. . .

1

I

 

 

 

 

 

 

 

 

 

 

 

 

I

(о]дает....

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

*

I

 

 

 

 

 

*

 

 

 

 

 

 

 

Future

 

 

I

Client

I

 

 

 

 

getResuLtO:ResuLt

 

 

 

 

 

Знает, существуетли объект, инкапсулирующий ....1

 

 

 

1

 

 

 

 

 

 

 

 

 

 

ре]ультат вычисления

 

 

 

 

0

 

 

 

 

 

 

 

Исполь]ует ·

 

 

*

ResuLt. . 1

I

 

 

 

 

 

 

 

 

 

Рис. 9.38. Шаблон Future

Result. Экземпляры класса в этой роли инкапсулируют результат вычисления.

Client. Экземпляры классов в этой роли используют объекты Resul t. ОбъектЫ

Cl ient инициируют процесс получения объекта Resul t, передавая соответст­

вующие параметры методу dol t объекта Requester.

Requester. Класс в этой роли имеет метод, вызываемый для инициирования вы­

числения объекта Resul t. На рис 9.38 этот метод показан как метод dol t . Ме­

тод dol t возвращает объект Future, который инкапсулирует это вычисление.

Future. Каждый экземпляр класса в этой роли инкапсулирует вычисление объ­

екта Resul t. Это вычисление может быть синхронным или асинхронным. Если

вычисление асинхронное, оно выполняется в своем собственном потою

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

инкапсулирующий его объект Future.

Объекты Future содержат метод, который вызывается объектами Cli,

получения результата вычисления. На рис. 9.38 этот метод указан как get

Способ работы этого метода зависит от того, синхронным или аСИНХI

является вычисление.

Если вычисление асинхронное и этот метод вызывается до окончания ВI

ния, то этот метод ожидает завершения вычисления и затем возвращает

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

Если вычисление синхронное, при первом вызове метода вычисление СI но завершается и возвращается его результат. Последующие обращения

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

На рис. 9.39 представлена диаграмма взаимодействия, деМОНСТРИРУЮЩi модействия между объектами в рамках шаблона Future. Опишем эти ,

действия.

1 .

Метод Client вызывает метод dolt объекта Requester. Тот инИr.I

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

dolt.

2.Затратив некоторое время на другие действия, объект Cl ient ВI метод getRe sul t объекта Future. Этот метод возвращает реЗУЛI:

числений.

-.

 

 

 

 

 

 

1: f :- doIt(params)

 

 

 

 

 

 

 

 

 

 

 

-.

 

 

 

 

2: r :- getResultsO

 

 

Рис. 9.39. Взаимодействие в шаблоне Future

РЕАЛИЗАЦИЯ

О п р о с

Общая схема выполнения шаблона Future предусматривает обращение

Cl ient к методу dolt объекта Requester, чтобы начать вычисление ,

как ему понадобится результат. Затем объект Client занят другими дейс

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

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

В некоторых ситуациях такая схема не действует. Например, когда у клиента есть активное задание, которое он постоянно выполняет. Рассмотрим пример сервлета, который генерирует Web-страницу, содержащую новости и информа­ цию о погоде. Предположим, что этот сервлет применяет связанные с погодой классы, рассмотренные в разделе «Контекст». После того как он запросил ин­ формацию о погоде, нельзя рассматривать ни один момент как самое послед­ нее возможное время получения информации о погоде. Сервлет непрерывно обновляет информацию, используемую им в качестве содержимого Web-cтpa­ ницы. Нужно, чтобы ни один запрос сервлета, предоставляющего содержимое Web-страницы, не был задержан, так как это приведет к задержке обновления информации о погоде.

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

Более удачное решение состоит в том, чтобы добавить еще один метод в класс Future. Как правило, имя этого метода начинается со слова «check» , например, checkComputation. Назначение метода check в том, чтобы проверять, будет ли объект Cl ient ждать результатов вычислений, если он вызовет метод getResul t объекта Future. Метод check может быть полезен в том случае, ко­ гда инкапсулированное вычисление является асинхронным. Метод check воз­ вратит false, если он вызывается до того, как закончилось вычисление, или true,

если он вызывается после завершения вычисления. Если вычисление синхрон­

ное, метод check всегда возвращает true.

Использование метода check позволяет объектам C l ient опрашивать объекты

Future. Каждый запрос на получение содержимого может опрашивать объект

Future, вызывая его метод check. Если метод check возвращает true, сервлет обновляет информацию о погоде, которая используется Web-страницеЙ. Если

метод check возвращает false, то сервлет продолжает использовать ту информа .

цию, которая уже имеется.

З а ме с тител ь

Шаблон Future иногда используется вместе с шаблоном Ргоху. Обычно это де­

лается так: класс, выступающий в роли Future, реализует интерфейс, который

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

При таком подходе объект, инициирующий вычислеот ние и создание оБЪeJ<K:tKта

Future, и объект, который получает результат объекта Future, - это, правило, разные объекты.

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