GrandM-Patterns_in_Java
.pdf290 • Глава 7. Структурные шаблоны проектирования
CacheManager. Объекты Client запрашивают объекты у объекта CacheManager, вызывая его метод fetchObj ect. Аргументом метода fetchObj ect СЛуЖlfr объект Obj ectKey, который идентифицирует считываемый объект. Сначалане метод fetchObj ect вызывает метод fetchObj ect объекта Cache. В случае удачи он вызывает метод createObject объекта Obj ectCreator.
ObjectCreator. Объекты Obj ectCreator отвечают за создание объектов, кото рые не находятся в кэше. КЭше.
Объект Cache отвечает за управление коллекцией объектов в
Если задан объект Obj ectKey, объект Cache быстро находит соответствующий объект, содержащийся в кэше. Объект CacheManager, чтобы получить объеК1' из кэша, передает объект Obj ectKey методу fetchObj ect объекта Cache. Если метод fetchObj ect не возвращает нужный объект, объект CacheManager про сит объект Obj ectCreator создать этот объект. Если объект Obj ectCreator возвращает затребованный объект, объект Cache передает полученный объеК1' методу addObj ect этого объекта. Метод addObj ect добавляет объект в кэш, если это согласуется с его политикой управления кэшем. Метод addObj ect мо жетудалить объект из кэша, чтобы освободить место для объекта, добавляемого в кэш.
РЕАЛИЗАЦИЯ
О п и с а н и е структу р ы
При проектировании обоих классов: и CacheManager, и Obj ectCreator - следует учесть то, что они должны реализовывать общий интерфейс. Объекты Client должны получать доступ к объектам CacheManager через общий ин терфейс, что делает использование кэша прозрачным для объектов Cl ient.
Если они реализуют общий интерфейс, то объекты Cl ient используют объект, который реализует тот же самый интерфейс, независимо от того, применяетсЯ ли кэширование или нет.
Этот интерфейс не упоминается в разделе «Решение как часть данного шабло
на, поскольку классы, выступающие в роли Obj ectCreator, часто проектирУ
ются до того, как начинает рассматриваться кэширование. Поэтому объекты ObjectCreator часто не реализуют какой-либо подходящий интерфейс.
Р е а л и з а ц и я к э ш а
Реализация шаблона Cache Management предусматривает принятие некоторЫХ118 потенциально сложных решений. Оптимальный вариант может быть выбран
основе обширного статистического анализа, теории массового обслуживанИЯ и других видов математического анализа. Однако обычно имеется возможносвэ1Ь осуществлять приемлемую реализацию, используя информацию о готовых
риантах и экспериментируя с различными решениями.
Cache Management - 291
При реализации шаблона Cache Management основное решение заключается
в том, как реализовать сам кэш. Приведем некоторые соображения по поводу |
|
выбора структуры данных для кэша: |
|
• |
кэш должен уметь быстро находить объекты по заданному для них Obj ectKey; |
• |
поиск будет производиться чаще добавления или удаления, поэтому он дол |
•жен выполняться так же быстро или быстрее добавления или удаления; предполагается частое использование операций добавления и удаления объ ектов, поэтому структура данных не должна рассматривать эти операции как требующие намного больше затрат, чем операции поиска.
Этим требованиям удовлетворяет хэш-таблица. Если кэш реализуется на языке
Java, то он обычно создается при помощи экземпляра класса j ava . util . HashMap
ИЛИ j ava . util . Hashtable .
Н а с т р о й ка п р о и з в од ител ь н о с т и к э ш а
Остальные вопросы реализации относятся к настройке производительности. Этой теме не следует уделять внимание до тех пор, пока программа не начнет правильно функционировать. На этапах проектирования и начального кодиро
вания следует задать первоначальные решения в отношении рассматриваемых вопросов, а затем просто не обращать на них внимания до тех пор, пока вы не
будете готовы рассматривать вопросы, связанные с производительностью.
Самый простой способ оценки эффективности кэширования состоит в том, чтобы вычислить некоторую статистическую величину, которая называется частотой успешных обращений (hit rate). Частота успешных обращений пред ставляет собой процент запросов на считывание объектов, которые были удов летворены кэш-менеджером, предоставившим объекты, хранящиеся в кэше. Если все запросы бьUIИ удовлетворены с помощью объекта, находящегося в кэше,
то частота успешных обращений составляет 100 %. Если ни один запрос не был
удовлетворен подобным образом, частота успешных обращений равняется О %. Частота успешных обращений в значительной степени зависит от Toro, на Сколько хорошо реализация шаблона Cache Management соответствует принци пу запрашивания объектов. для
Всегда существует максимальный размер памяти, которую можно выделить kЭша. Это означает, что нужно задать предельное количество объектов, кото Рые могут находиться в кэше. Если предполагаемое количество таких объектов liевелико, то не следует задавать явный предел. Но большинство проблем не "меет такого простого решения.
ТаКПредварительно задать максимальные размеры отводимой дЛя кэша памяти не легко, поскольку можно не знать ззранее, сколько будет свободной памяти I1ли сколько памяти потребуется дЛя остальной части программы. Задать пре дельные размеры памяти, выделяемой дЛя кэша, особенно проблематично в языке
J11ava, поскольку не существует определенного соотношения между объектом
размером физической памяти, которую он занимает.
292 • Глава 7. Структурные шаблоны проектирования
Альтернативой заданию и установлению предельных размеров памяти мож служить простой подсчет объектов, который производится очень легко, поэтом можно упростить задачу, ограничивая содержимое кэша определенным коли •
чеством объектов.
Конечно, если сушествует ограничение на размеры кэша, то неизбежен вопрос! что случится, когда размеры кэша будут соответствовать максимальному разре шенному количеству объектов и при этом создается новый объект. С этого мо мента кэш должен хранить на один объект больше, чем предполагалОСIi! И кэш-менеджер должен отбросить некий объект. Выбор удаляемого из кэша объекта очень важен, так как он прямо воздействует на частоту успешных обра
шениЙ. Если отбрасываемый объект всегда будет следуюшим запрашиваемым объектом, то частота успешных обрашений будет равна О %. С другой стороны, если отброшенный объект не потребуется раньше всех остальных, находяшихся в кэше объектов, то отбрасывание этого объекта окажет минимальное негативное воздействие на частоту успешных обрашениЙ. Очевидно, что правильный выбор отбрасываемого объекта требует прогнозирования будуших запросов объектов.
В некоторых случаях, зная область применения, можно сделать разумное пред положение по поводу того, какие объекты потребуются программе в ближайшем будушем. В наиболее благоприятных ситуациях можно с высокой степенью веро ятности предсказать, какой именно объект будет затребован следуюшим. В таких случаях, если объект еше не находится в кэше, то он может быть срочно асин хронно создан в первую очередь, не дожидаясь запроса к нему со стороны про граммы. Это называется выборкой объекта с упреждением.
В большинстве случаев область применения не предоставляет достаточной ин формации для выполнения таких точных предсказаний. Однако сушествует шаблон, встречаюшийся так часто, что он стал основой эффективной задаваэтае мой по умолчанию стратегии принятия решения об отбрасывании объекта.
стратегия основывается на том, что чем меньше прошло времени с тех пор, как программа запрашивала некоторый объект, тем больше вероятность, что она запросит его снова. Таким образом, всегда отбрасывается объект кэша, менее всех использовавшийся за последнее время. эту стратегию часто сокрашеннО называют LRU (Least Recently Used, использовавшийся наиболее давно).
А теперь рассмотрим задание числового предела для находяшихся в кэше объ ектов. Математический анализ может предоставить точное значение для мак симального количества объектов, помешаемых в кэш. Но подобный аналИЗ обычно не применяется по двум причинам. Первая состоит в том, что матема тический анализ предполагает использование теорий вероятностей и MaccoBOfO
обслуживания, о которых большинство программистов ничего не знают. Другая
причина заключается в том, что такой анализ может потребовать недопустимО
много времени. Должно быть собрано очень много информации о программе и ее операционной среде. Тем не менее приемлемые размеры кэша обыЧJ{О
можно вычислить эмпирическим путем.
Сначала добавим код в класс CacheManager с целью оценки частоты успешнЫ"
обращений , рассматриваемой как отношение количества запросов объектоВ.
Cache Management • 293
удовлетворенных при помощи кэша, к общему количеству запросов объектов. Затем можно попробоватьэти поработать с различными предельными размерами объектаэто . Выполнив действия, можно заметить, что, если кэш слишком велик, может привести к замедлению или полному прекращению работы остальной
части программы. Программа может не работать из-за нехватки памяти.
Предположим, что нужно настроить программу, использующую кэш. Запус кают программу в идентичных условиях, задавая различные максимальные раз меры кэша. Допустим, задают значение 6000 объектов и отмечают, что при 6000 на выполнение программы требуется примерно в три раза больше времени, чем пныри 4000. Это значит, что 6000 - слишком большое значение. В табл. 7.1 указа
возможные значения частоты успешных обращений, которые можно полу чить для разных значений размеров кэша.
Таблица 7.1. Размеры кэша и частота успешных обращений |
||||
|
|
Максимальный размер кэша, объектов |
Частота успешных |
обращений, % |
250 |
20 |
|
||
500 |
60 |
|
||
1000 |
ВО |
|
||
2000 |
90 |
|
||
3000 |
9В |
|
||
4000 |
100 |
|
||
|
5000 |
100 |
|
Очевидно, что нет необходимости делать размер кэша больше, чем на 4000 объ екто100 в, поскольку уже при этом значении частота удачных обращений достигает
4000%. В данных условиях запуска программы идеальный размер кэша равен объектам. Если программа будет рабorать точно в таких условиях, то даль вlIейшая настройка может не понадобиться. Если программа будет работать друmх условиях, можно использовать кэш меньших размеров, чтобы избе
>кать проблем в случае меньших размеров свободной памяти. Выбранное число дОлжно представлять собой компромисс между предполагаемой высокой часто Той успешных обращений и желательно небольшим размером кэша. Поскольку УМеньшение размеров кэша до 3000 объектов снижает частоту успещных обра щений только до 98 %, то значение 3000 может отражать приемлемый размер I(ЭUJа. А если приемлемая частота успещных обращений равна 90 %, то пример IIый размер кэша составляет 2000.
!,fbrxЕсли невозможно достичь большой частоты успешных обращений при приемле размерах памяти, а создание объектов требует больших затрат, то нужно Рассмотреть вопрос об использовании вторичного кэша. Обычно вторичный
ЭUJ представляет собой файл на диске, длякоторый используется в качестве кэша.
ICJIТоричныйВ кэш требует больше времени ДОС1уПа, чем первичный, находящий
памяти. Однако для выборки объектов из файла локального диска требуется
294 • Глава 7. Структурные шаблоны проектирования
значительно меньше времени, чем для повторного создания этих объектов при помоши исходного источника данных; поэтому предпочтительнее использо вать вторичный кэш, чем не использовать его.
Вторичный кэш используют следуюшим образом: при заполнении основного
кэша объекты не отбрасываются, а перемещаются во вторичный кэш.
Вз а и мо д е й ст в и е с п ро гр аммо й с б о р к и му с о р а
Задание постоянного ограничения количества объектов, находящихся в кэшене,
может являться эффективным способом обеспечения гарантии, что кэшу
потребуется дополнительный объем памяти. С другой стороны, имея возможне ность выбирать количество, которое, как полагают, достаточно мало, чтобы
мешать другим потребителям памяти, можно задавать размер кэша, соответст
вуюший худшему случаю. Задавая размеры кэша, соответствующие худшему
случаю, можно уменьшить производительность, которая соответствует средне
му случаю. Было бы хорошо иметь способ задавать размеры кэша, соответст вуюшие среднему случаю, а затем удалять объекты из кэша, если память нужна для других целей.
И оказалось, что такой способ есть. Он предусматривает работу с программой сборки мусора, которая может удалять объекты из кэша, если во время работы NM обнаруживается нехватка памяти.
Программа сборки мусора имеет специальную связь с классом j ava . lang . ref . SoftReference. Ссылка на другой объект передается конструктору объ екта SoftReference. Как только объект SoftReference будет создан, его ме тод get возвратит объектную ссылку, которая была передана его конструктору.
Если единственная активная ссылка на объектдляимеется в объекте SoftRe ference и память, занимаемая объектом, нужна других целей, то програм
ма сборки мусора будет устанавливать ссылку в объекте So ftReference в пuП
с тем, чтобы могла быть безопасно освобождена память, занимаемая объектом,
на который указывает эта ссылка.
На рис. 7.33 показано участие класса SoftReference и связанного с ним клас
са j ava . lang . ref . ReferenceQueue в шаблоне Cache Management. Объект
Cache не ссылается прямым образом на тот объект, который в нем содержится.
Вместо этого он связывает каждый объект Obj ectKey С объектом SoftKeyRe ference, первоначально содержащим ссылку на объект, идентифицируемый
с помощью объекта Obj ectKey.
Класс объекта SoftKeyReference является подклассом класса j ava . lanсоg .
ref . SoftReference. Если создается объект SoftKeyReference, он будет
держать ссьшку на находящийся в кэше объект и ссылку на объект Obj ectKмоey,
который идентифицирует объект, содержащийся в кэше. Если в какой-то
мент времени программа сборки мусора решает, что ей нужна память, которУЮ
занимает находящийсЯ в кэше объект, и нет другой ссьшки на объект, содержа-
щийся в кэше, то программа сборки мусора очистит в объекте SoftKeyRe ference ссылку (установит ее в пиН) на объект, хранящийся в кэше. ПрограмМ8
|
|
|
|
|
|
|
|
|
|
|
|
Cache Management |
_ |
295 |
||||||||
|
|
|
|
|
|
|
|
|
|
|
Пуп ДIIхранения объектовSoftReference |
|
|
|||||||||
|
|
CLient |
I |
|
|
|
|
|
|
|
|
|
|
|
|
1 |
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
ReferenceQueue |
|
|
|
||||||||
0. • * |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
|
|
|
|
|
|
|
....Считываетобъект |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
1 |
|
|
|
|
|
|
|
|
|
|
|
Регистрируется.... |
|
|
|
|
||||||
|
CacheManager |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
fetchObject(:ObjectKey) |
|
|
|
|
|
Cache |
|
|
|
|
|
|
|
|
|
|||||||
1 |
|
в кэш |
|
|
1 |
|
1 |
|
addObject( :Object ) |
|
|
|
* |
|
|
|
|
|
||||
|
|
помещающий I |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
Помещает объектв кэш |
|
fetchObject( :ObjectKey ) |
|
|
|
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
SoftReference |
|
||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
Помещает в кэш - I |
I |
|||||||||
|
|
|
|
|
|
|
|
|
1 |
О |
|
|
|
j |
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||
|
|
|
-Создаетобъекты ДIIпЯомещения их в кэш |
ObjectKey I |
|
|
|
|
|
|
|
|
|
|
||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
0• . * |
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
L------- |
|
I |
|
I |
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||
|
|
|
|
|
|
|
|
|
|
|
|
....Ссыпается на |
1 |
|
SoftKeyReference |
|
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
1 |
|
|
считывающий |
|
|
|
|
|
|
|
-Ссыпается на |
1 . |
|
|
|
|||||||
|
|
|
ObjectCreater |
|
|
|
|
|
|
|
|
|
|
|
. |
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
I |
0. |
|
|
|
I |
|||||||
createObject(:ObjectKey) |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Object |
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Рис. 7.33. Управление кэшем, осуществляемое с помощью класса SoftReference
сборки мусора обязательно должна выполнять очистку ссылки, перед тем как
освободить память, занимаемую объектом.
Если объект Cache пытается получить содержащийся в кэше объект с помощью
объекта SoftKeyRe ference после того, как тот был очищен, он получает пиН.
Поскольку объект Cache генерирует пиН, если хочет сказать, что в кэше нет
больше объектов, соответствующих данному объекту Obj ectKey. очистка объ
еКТа SoftKeyReference свидетельствует об удалении объекта из кэша, даже
если ObjectKey все еще находится в кэше. При помощи этого механизма объ ект Obj ectKey действительно в конце концов удаляется из кэша.
296 • Глава 7. Структурные шаблоны проектирования
Когда создается объект S oftKe yReference, он регистрируется при помощи
объекта ReferenceQueue объекта Cache. Если программа сборки мусора очи
шает ссылку в объекте SoftKeyReference, объект SoftKeyRe ference (по
скольку он регистрировался при помощи объекта ReferenceQueue объекта
Cache) ставится в очередь в объекте ReferenceQueue. Каждый раз, когда объ
екту CacheManager поступает запрос на создание объекта, он опрашивает объ
ект ReferenceQueue с целью проверки объектов SoftKeyRe ference, стоящих в очереди.
Сушествует некоторый объект Obj ectKey, связанный с каждым объектом
SoftKeyReference. Когда объект CacheManager получает объект So ftKeyRe ference из своего объекта ReferenceQueue, он удаляет объект Obj ectKey,
связанный с объектом SoftKeyReference, из объекта Cache.
Иногда приложениякакшаблона Cache Management добавляются в проект про граммы после того, была обнаружена необходимость оптимизации произ водительности. Обычно это не представляет большой проблемы, так как влия ние шаблона Cache Management на остальную часть программы минимально. Если доступ к рассматриваемым объектам уже реализован с применением шаб лона Virtual Рюху, реализация шаблона Cache Management может быть поме щена в класс заместителя, не требуя изменения других классов.
@) Основным следствием применения шаблона Cache Management является то, что программа затрачивает меньше времени на получение объектов.
@) Если объекты создаются на основе данных из внешнего источника, то дру гим следствием использования шаблона Cache Management является то, что
данные, хранящиеся в кэше, могут стать несовместимыми с исходным ис
точником данных. Проблема совместимости подразделяется на две отдель
ные проблемы, которые могут быть решены независимо друг от друга. ОНИ
получили названия согласованности чтения и согласованности записи.
Согласованность чтения означает, что в кэше всегда отражены последние изме
нения информации, произошедшие с исходным источником объектов. Если объекты, находящиеся в кэше, содержат информацию о курсе акций, то цены,
указанные в источнике объектов, могут изменяться, и тогда цены, хранимые
в кэше, больше не будут отражать текушее состояние.
Согласованн |
|
а о |
|
|
ость записи означает, что исходный источник объектов всегд |
т |
|
ражает последние изменения в кэше. |
|
|
Чтобы достичь абсолютной согласованности чтения или записи находяшихся в кэше объектов с исходным источником объектов, необходимо реализоватЬ
механизм их синхронизации. Подобные механизмы могут реализовыватьсЯ очень сложно и приводить к значительному увеличению времени выполнениЯ. Как правило, они используют такие способы, как блокировка и оптимизациоН
ная параллельность, которые не рассматриваются в данной книге. Эти вопросъl