Многопоточное использование итераторов ConcurrentHashMap

Мне нужно написать несколько специфическую реализацию кеша, который имеет уникальные ключи, но может содержать повторяющиеся значения, например:

 "/path/to/one" -> 1
 "/path/to/two" -> 2
 "/path/to/vienas" -> 1
 "/path/to/du" -> 2

Класс должен предлагать неблокирующее чтение / поиск ключей, но также имеет типичный create / обновить / удалить мутаторы. Например, удаление значения 2 должно привести к

"/path/to/one" -> 1
"/path/to/vienas" -> 1

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

Итак, я написал что-то вроде этого (псевдокод):

//
// tl;dr all writes are synchronized on a single lock and each
// resets the reference to the volatile immutable map after finishing
//
class CopyOnWriteCache {
   private volatile Map<K, V> readOnlyMap = ImmutableMap.of();

   private final Object writeLock = new Object();

   public void add(CacheEntry entry) {
      synchronized (writeLock) {
         readOnlyMap = new ImmutableMap.Builder<K, V>()
            .addAll(readOnlyMap)
            .add(entry.key, entry.value)
            .build();
      }
   }

   public void remove(CacheEntry entry) {
      synchronized (writeLock) {
         Map<K, V> filtered = Maps.filterValues(readOnlyMap, somePredicate(entry));
         readOnlyMap = ImmutableMap.copyOf(filtered);
      }
   }

   public void update(CacheEntry entry) {
      synchronized (writeLock) {
         Map<K, V> filtered = Maps.filterValues(readOnlyMap, somePredicate(entry));
         readOnlyMap = new ImmutableMap.Builder<K, V>()
             .addAll(filtered)
             .add(entry.key, entry.value)
             .build();
      }
   }

   public SomeValue lookup(K key) {
      return readOnlyMap.get(key);
   }
}

После написания вышеизложенного я понял, что ConcurrentHashMap также предлагает неблокирующие чтения, которые сделают все мои усилия бессмысленными, но есть утверждение в его Javadoc, который поднимает бровь:

iterators are designed to be used by only one thread at a time

Итак, если я заменю использование volatile ImmutableMap на final ConcurrentHashMap и удалю все синхронизированные блоки, возможно ли это что конкурирующие параллельные мутаторы аннулируют друг друга? Например, я могу представить, как два одновременных вызова remove приведут к состоянию гонки, полностью аннулируя результаты первого remove .

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

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

5
задан mindas 19 January 2012 в 22:35
поделиться