Как обновить кэш из нескольких потоков - PullRequest
0 голосов
/ 29 ноября 2018

У меня есть Runnable с кешем (типа Cache ), и мы предполагаем, что он предлагает поточно-ориентированные операции.Этот объект Runnable используется несколькими потоками.

Наши потоки получают объекты из внешнего источника, а затем

  1. проверяют, существует ли ключ объекта в кэше
  2. Если нет, то положить
  3. Если он уже находится в кеше, то обновить

Я ищуПравильная схема (т. е. минимальный synchronized код) для надежной работы с кешем.

Я придумал следующую схему:

    MyObject current = cache.getIfPresent(givenKey);
    if (current == null) {
        MyObject prev = cache.asMap().putIfAbsent(givenKey, givenObj);
        if (prev == null) {
            // successful put in cache
            return givenObj;
        }
    }

    // current != null or another thread update
    synchronized (current) {
        return update(current, givenObj); // in place change of current
    }

Ключевые идеи моей схемы + "доказательство"«Надежность:

  1. Если потоки работают на разных ключах, нет необходимости блокировать
  2. Если current равен null, то, поскольку кэш является поточно-ориентированным, ровно одинпоток сможет поместить объект в кеш, в то время как другие увидят prev != null
  3. Другие потоки должны обновляться последовательно.Обратите внимание, что я синхронизируюсь на current, объект, который будет обновлен.

Вопросы

  1. Надежна ли моя схема?
  2. Можно оптимизировать?
  3. В некоторых случаях volatile необходимо использовать для обеспечения надежной синхронизации памяти.Мне нужно здесь?

Спасибо!

Ответы [ 2 ]

0 голосов
/ 30 ноября 2018
  1. проверить, существует ли ключ объекта в кеше
  2. Если нет, то поставить
  3. Если он уже в кеше, обновить

Это может быть выполнено с использованием метода представления Map view .

cache.asMap().compute(givenKey, (key, oldValue) -> {
  return (oldValue == null)
      ? create(key)
      : update(current, oldValue);
});

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

К сожалению, это не оптимизировано в Guava, так как оно было добавлено при добавлении совместимости с Java 8 для его дополнений.Вы должны предпочесть 27.0.1 или новее, так как были некоторые неприятные ошибки.

Caffeine - это перезапись Java 8, предназначенная для этой функции.Он основан на уроках, извлеченных из Guava (аналогичные интерфейсы, но пересматривает некоторые варианты дизайна), современных алгоритмов исследования и улучшения экосистемы.Оба отлично, но вы можете найти кофеин лучше подходит для более сложных сценариев.

0 голосов
/ 29 ноября 2018

1) нет, ваша схема ненадежна. Вы не должны вызывать

cache.asMap().putIfAbsent(givenKey, givenObj);

с помощью метода документации guava. Cache.get (ключ K, вызываемый загрузчик) предпочтительнее, чем использовать методы asMap.

2) да, его можно оптимизировать. Вместо этого следует вызвать этот метод:

cache.get(K key, Callable<? extends V> loader)

Этот метод вернет значение, если оно уже находится в кеше, или добавит значение из загрузчика в кеш, еслизначение не находится в кеше и возвращает его.

, например:

 MyObject objInCache =  cache.get(givenKey, ()->givenObj)

if(!objInCache.equals(givenobj)){
 //obje was in the cache,
//update object
}

3) вам не нужно использовать volatile, если кеш является поточно-ориентированным

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