Ehcache - используя список <Integer>в качестве значения кэша - PullRequest
6 голосов
/ 10 ноября 2010

Итак, вот проблема, которую я пытаюсь решить - у меня есть Объект с двумя целочисленными полями, которые я хочу кэшировать

public class MyObject {
   int x;
   int y;
   ....
}

Теперь поле x - это то, с чем я в основном сопоставляюсь - ноМогут быть дубликаты, в этом случае я хочу вернуться ко второму полю (так что this.x = that.x и this.y = that.y).у может быть только 25 различных значений.Теперь я знаю, что я мог бы просто объединить их в виде строки и использовать их в качестве ключа кеша, но тогда мне пришлось бы попробовать x+[25 possible values], чтобы на самом деле определить, не было ли оно в кеше, - кеш пропускает очень дорого.Я думал о том, чтобы попытаться сохранить List<Integer> в качестве значения кэша для поля x, а затем, если их было больше единицы, выполнить итерацию вниз по списку и найти совпадение для y.

Теперь, если я использую ConcurrentList (или Set, если мне небезразличны дубликаты - давайте пока проигнорируем это), смогут ли несколько потоков добавить к нему и затем вернуть его в кеш без условий гонки?Возможно ли, что Ehcache может вернуть два разных объекта List двум потокам, а затем, когда они добавят свое новое значение в список и попытаются вернуть его в кэш, я могу получить неопределенные результаты?Видите ли вы лучший способ построения этого кэша?

РЕДАКТИРОВАТЬ: Я ценю ответы ниже, но, похоже, все упускают главное.Будет ли это работать?Может ли Ehcache на самом деле вернуть два разных объекта для одного и того же cacheKey (скажем, если объект находился на диске во время вызова и сериализовал его дважды, один раз для каждого вызова).

Ответы [ 5 ]

5 голосов
/ 18 ноября 2010

Вполне возможно, что вы получите два разных экземпляра вашего Списка (или любого Сериализуемого)!Попробуйте это:

public static void main(final String[] args) throws Exception {
    final Cache cache = CacheManager.getInstance().getCache("smallCache");

    final List<String> list = new ArrayList<String>();
    cache.put(new Element("A", list));

    /* We put in a second element. Since maxElementsInMemory="1", this means
     * that "A" will be evicted from memory and written to disk. */
    cache.put(new Element("B", new ArrayList<String>())); 
    Thread.sleep(2000); // We need to wait a bit, until "A" is evicted.

    /* Imagine, the following happens in Thread 1: */
        final List<String> retrievedList1 =
                   (List<String>) cache.get("A").getValue();
        retrievedList1.add("From Thread 1");

    /* Meanwhile, someone puts something in the cache: */
        cache.put(new Element("C", new ArrayList<String>())); 

    Thread.sleep(2000); // Once again, we wait a bit, until "A" is evicted.

    /* Now the following happens in Thread 2: */
        final List<String> retrievedList2 =
                   (List<String>) cache.get("A").getValue();
        retrievedList2.add("From Thread 2");
        cache.put(new Element("A", retrievedList2));

    /* Meanwhile in Thread 1: */    
        cache.put(new Element("A", retrievedList1));

    /* Now let's see the result: */
    final List<String> resultingList =
                        (List<String>) cache.get("A").getValue();
    for (final String string : resultingList) {
        System.out.println(string);
    } /* Prints only "From Thread 1". "From Thread 2" is lost.
                 But try it with maxElementsInMemory="3", too!! */

    CacheManager.getInstance().shutdown();
}

Я использовал следующее в ehcache.xml:

<cache name="smallCache"
       maxElementsInMemory="1"
       eternal="true"
       overflowToDisk="true"
       diskPersistent="true"
       maxElementsOnDisk="200"
       memoryStoreEvictionPolicy="LRU"
       transactionalMode="off"
       >
</cache>

Одним из решений может быть использование Явная блокировка , которая кажется доступнойдля автономных (не терракотовых) кешей тоже (начиная с ehcache 2.1).

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

2 голосов
/ 10 ноября 2010

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

Поместите две пары ключ-значение в кэш: одну с ключом только x, а другую с ключом x и y. Когда вы смотрите в кеш, сначала найдите ключ x-and-y. Если это там, вы нашли идеальную пару. Если его там нет, найдите ключ x и, возможно, найдите совпадение с другим значением y.

1 голос
/ 16 ноября 2010

Я бы создал метод для получения значения для вашего объекта. Используйте семафор, чтобы ограничить доступ к методу (или используйте синхронизированный).

В вашем методе проверьте на совпадения только по X, и, если он возвращает несколько результатов, текст для совпадений по XY.

Как только объект находится вне кэша, любые модификации объекта также изменят объект в кэше (так как они указывают на один и тот же экземпляр).

Если вы хотите быть очень осторожным, используйте синхронизированные методы для получения / установки переменных-членов в MyObject и включите блокировку, которая является экземпляром MyObject.

public void setX( int x ) {
     synchronized( this ) {
         this.x = x;
     }
}
0 голосов
/ 18 ноября 2010
  • Создайте ключ класса x и y, т.е. class Key { int x,y }
  • реализовать отдельную операцию сравнения для вас "лексический порядок" на x и y,
  • положить его в Map<Key,Value>
0 голосов
/ 10 ноября 2010

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

Я полагаю, в API коллекции Google есть много полезных вещей, которые вы можете использовать, например, SortedSetMultimap:

http://google -collections.googlecode.com / svn / trunk / javadoc / com / google / common / collect / SortedSetMultimap.html

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