1. Основные возможности
2. Требования к runtime Java
3. Что нужно для использования cache4j ?
4. Основные интерфейсы cache4j
5. Реализации
6. Вытеснение объектов
7. Время нахождения объекта в кеше
8. Хранение объектов в кеше
9. Файл конфигурации
10. Пример использования
 
Основные возможности
  • кеширование объектов в памяти
  • спроектирован для работы в многопоточных приложениях
  • две реализации кеша synchronized, blocking
  • реализовано алгоритмы вытеснения объектов LFU, LRU, FIFO
  • для хранения объектов могут использоваться сильные (strong) или магкие(soft) ссылки
  • распостраняется под BSD лицензией
Требования к runtime Java
cache4j можно использоваться с JDK 1.3, 1.4, 1.5
При использовании с JDK 1.3 нужен xml парсер (например: Apache xerces)
Что нужно для использования cache4j ?
  • cache4j_?.jar
  • файл конфигурации
  • xml парсер (если используется JDK 1.3)
Основные интерфейсы cache4j
  • CacheFactory этот класс управляет экземплярами кешей (загрузка конфигурации, добавление, удаление, ...)
  • Cache интерфейс доступа к объектам кеша
  • CacheConfig конфигурация кеша
  • CacheInfo предоставляет дополнительную информацию о кеше
  • ManagedCache интерфейс управления кешем

Документацию в формате javadoc можно найти здесь.

Реализации

В cache4j существует несколько реализаций которые определяют поведение кеша:

  • synchronized - в этой реализации доступ к объектам синхронизирован на уровне кеша. Это означает что в любой момент времени доставать, помещать или удалять объект из кеша может только один поток.
  • blocking - в этой реализации доступ к объектам синхронизирован на уровне объектов. Это означает что потоки будут переходить в режим ожидания только если они будут пытаться выполнить какое либо действие над одним и тем же объектом. Например если один поток запросил объект которого нет в кеше и после этого начал его загружать то другой поток, который обращается к тому же объекту, перейдёт в режим ожидания до того момента пока первый поток не поместит загруженный объект в кеш.
  • nocache - в этой реализации кеширования нет вообще. Эта реализация может быть полезна когда кеширование нужно отключить (например для того чтобы проверить производительность приложения без кеша)
Вытеснение объектов

Размер кеша ограничен поэтому при заполнении кеша из него нужно удалять какие то объекты. Критериями заполнения кеша являются количество объектов в кеше(max-size) и\или объём памяти занимаемый кешем(max-memory-size). Какие именно объекты нужно удалять определяется алгоритмом удаления(вытеснения). В cache4j реализованы следующие алгоритмы вытеснения:

  • lru - (Least Recently Used) удаляется объект к которому дольше всего небыло обращений
  • lfu - (Least Frequently Used) удаляется объект к которому было наименьшее количество обращений
  • fifo - (First In First Out) удаляется объект который дольше всего находится в кеше
Время нахождения объекта в кеше

Нахождение объектов в кеше можно контролировать параметрами:

  • ttl - максимальное время жизни объекта в кеше
  • idle - максимальное время бездействия объекта в кеше. Обычно меньше чем время жизни объекта.

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

Хранение объектов в кеше

Для хранения объектов в кеше могут использоваться strong или soft ссылки.

  • strong - ссылки это обычные ссылки на java объект
  • soft - этот тип ссылки предполагает доступ к объекту кеша через мягкую ссылку java.lang.ref.SoftReference. Зачем это нужно ? Если ссылка на кешируемый объект доступна только через java.lang.ref.SoftReference то garbage collector может удалить такой объект, но только в том случае если использована вся память доступная JVM. То есть если JVM требуется память но вся доступная память занята то garbage collector, кроме всего прочего, удаляет все объекты доступные только через java.lang.ref.SoftReference и если после этого память не освободилась бросает исключение java.lang.OutOfMemoryError. Такое поведение полезно когда в кеше нужно держать максимально возможное количество объектов и в тоже время предоставлять приложению возможность использовать всю доступную память.
Файл конфигурации

<cache-factory>

<cache-factory> это корневой тег в файле конфигурации. Возможные атрибуты тега описаны в таблице ниже.

Атрибут По умолчанию Описание
clean-interval 30 секунд

Период очистки кеша от устаревших объектов. Значение можно указывать в:

  • миллисекундах (60000 = 1минута)
  • секундах (60s = 60 секунд)
  • минутах (1m = 1 минута)
  • часах (1h = 1 час)

<cache>

Тег <cache> описывает конфигурацию одного экземпляра кеша. Возможные атрибуты тега описаны в таблице ниже.

Атрибут По умолчанию Описание
id атрибут обязателен для заполнения Идентификатор кеша, должен быть уникальным. В CacheFactory не может существовать два кеша с одинаковым идентификатором.
desc null Описание экземпляра кеша.
ttl 0 - без ограничений Максимальное время жизни объекта в кеше (Time To Live). Значение можно указывать в:
  • миллисекундах (60000 = 1минута)
  • секундах (60s = 60 секунд)
  • минутах (1m = 1 минута)
  • часах (1h = 1 час)
idle 0 - без ограничений Максимальное время бездействия объекта в кеше (Idle Time). Значение можно указывать в:
  • миллисекундах (60000 = 1минута)
  • секундах (60s = 60 секунд)
  • минутах (1m = 1 минута)
  • часах (1h = 1 час)
max-memory-size 0 - без ограничений

Максимальный объём памяти занимаемый всеми объектами кеша(байт). Значение можно указывать в:

  • байтах (1024 = 1 килобайт)
  • килобайтах (1k = 1 килобайт)
  • мегабайтах (1m = 1 мегабайт)
max-size 0 - без ограничений Максимальное количество объектов в кеше.
type synchronized

Тип кеша. Возможные значения:

  • blocking
  • synchronized
  • nocache
algorithm lru

Алгоритм удаления объектов из кеша. Возможные значения:

  • lru (Least Recently Used)
  • lfu (Least Frequently Used)
  • fifo (First In First Out)
reference strong

Тип ссылки на объект содержащийся в кеше. Возможные значения:

  • strong сильная ссылка
  • soft магкая ссылка (SoftReference)
Пример использования

Задача: добавить кеширование в DAO для объектов "Счёт" и "Валюта" кроме этого создать кеш в котором будут кешироваться временные объекты.
  Количество объектов "Валюта" ограничено примерно 100шт., поэтому все валюты можно держать в кеше без каких либо ограничений. Количество и размер временных объектов не известно, поэтому кеш для таких объектов можно ограничить по занимаемому объёму и\или по количеству объектов. Кроме этого для хранения временных объектов в кеше можно использовать мягкие ссылки. Возможное количество объектов "Счёт" как и количество временных объектов не определено, поэтому для хранения счетов так же можно использовать кеш с ограничениями по количеству или занимаемому объёму. Объекты "Счёт" содержат ссылку на объект "Валюта" поэтому после получения "Счёта" из кеша нужно обновить ссылку на валюту, так как "Валюта" могла изменится.

Файл конфигурации:
  <?xml version="1.0" encoding="Windows-1251"?>
          <!--
           | Интервал удаления устаревших объектов : 30 секунд
           -->
    <cache-factory clean-interval="30s">
          <!--
           |   Кеш для временных объектов 
           |   время жизни объектов       : 1 час
           |   время бездействия объектов : 30 минут
           |   количество объектов        : 1000
           |   максимальный объём памяти  : 50 мегабайт
           |   тип ссылки на объекты      : soft(мягкая ссылка) 
           -->
        <cache id="tempObj" desc="Кеш для временных объектов"
            ttl="1h"                  
            idle="30m"
            max-size="1000"
            max-memory-size="50m"
            reference="soft" />

         <!--
          |   Кеш для объектов "Валюта"
          -->
        <cache id="currency" desc="Кеш для объектов Валюта"/>

          <!--
           |   Кеш для объектов "Счёт"
           |   количество объектов        : 1000
           -->
        <cache id="account" desc="Кеш для объектов Счёт"
            max-size="1000" />

    </cache-factory>
Загрузка конфигурации:
    import  net.sf.cache4j.CacheFactory;
    import  net.sf.cache4j.Cache;
    import java.io.InputStream;
    import java.io.FileInputStream;
    //...
    CacheFactory сacheFactory = CacheFactory.getInstance();
    try {
        InputStream in = new FileInputStream("config.xml");
        сacheFactory.loadConfig(in);
    } catch (CacheException ce){
        // ...
    }
    //...
Получение экземпляра кеша:
    //...
    Cache tempObjCache = CacheFactory.getInstance().getCache("tempObj");
    Cache accountCache = CacheFactory.getInstance().getCache("account");
    Cache currencyCache = CacheFactory.getInstance().getCache("currency");	
    //...
Загрузка "Валюты":
    //...
    public Currency loadCurrency(String id) throws Exception {
        Currency currency = null;
        try {
            //пробуем достать валюту с идентификатором id из кеша
            currency = (Currency)currencyCache.get(id);
        } catch (CacheException ce) {
            //...
            throw new Exception(ce);
        }
        //если объект достали из кеша
        if (currency != null) {
            return currency;
        }
        try {
            //если объекта не было в кеше его нужно подгрузить из 
            //базы а потом поместить в кеш
            currency = loadCurrencyFromDb(id);
        } finally {
            try {
                currencyCache.put(id, currency);
            } catch (CacheException ce) {
                throw new Exception(ce);
            }
        }
    }
Загрузка "Счёта":
    //...
    public Account loadAccount(String number) throws Exception {
        Account account = null;
        try {
            //пробуем достать счёт с идентификатором number из кеша
            account = (Account)accountCache.get(number);
        } catch (CacheException ce) {
            //...
            throw new Exception(ce);
        }
        //если объект достали из кеша
        if (account != null) {
            return updateAccount(account);
        }
        try {
            //если объекта не было в кеше его нужно подгрузить 
            //из базы а потом поместить в кеш
            account = loadAccountFromDb(number);
        } finally {
            try {
                accountCache.put(number, account);
            } catch (CacheException ce) {
                throw new Exception(ce);
            }
        }
    }
    public Account updateAccount(Account account) throws Exception {
        //обновляем ссылку на валюту
        account.setCurrency(loadCurrency(account.getCurrencyId()));
        return account;
    }

Сохранение "Валюты":
    public Currency saveCurrency(Currency currency) throws Exception {
        saveCurrencyInDB(currency);
        try {
            currencyCache.remove(currency.getId());
        } catch (CacheException ce) {
            throw new Exception(ce);
        }
        return currency;
    }
Сохранение "Счёта":
    public Account saveAccount(Account account) throws Exception {
        saveAccountInDB(account);
        try {
            accountCache.remove(account.getNumber());
        } catch (CacheException ce) {
            throw new Exception(ce);
        }
        return account;
    }