necesito escribir una aplicación un tanto específica de una memoria caché, que tiene las claves únicas, pero puede contener valores duplicados, por ejemplo:uso de multiproceso de `iteradores ConcurrentHashMap`
"/path/to/one" -> 1
"/path/to/two" -> 2
"/path/to/vienas" -> 1
"/path/to/du" -> 2
La clase tiene que ofrecer no lee bloqueo/búsquedas clave, pero también tiene mutadores típicos de crear/actualizar/eliminar. Por ejemplo, el valor de la eliminación de 2
debería resultar
"/path/to/one" -> 1
"/path/to/vienas" -> 1
Lecturas para este caché serán mayores que las escrituras, con mucho, por lo que el rendimiento de escritura no es un problema - siempre y escrituras concurrentes no se ejecutan en uno encima del otro. Es probable que la cantidad total de entradas sea inferior a 1000, por lo que la iteración ocasional de los valores sigue siendo asequible.
así que escribí algo como esto (pseudo código):
//
// 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);
}
}
Después de escribir lo anterior, me di cuenta de que ConcurrentHashMap
también ofrece sin bloqueo lee lo que haría que todo mi esfuerzo inútil, pero hay una declaración en su Javadoc, que levanta una ceja:
iterators are designed to be used by only one thread at a time
Así que si puedo reemplazar el uso de volatile ImmutableMap
con final ConcurrentHashMap
y eliminar todas las synchronized
bloques, es posible que compiten mutators concurrentes se invalidar el uno al otro? Por ejemplo, puedo imaginar cómo dos llamadas concurrentes a remove
conducirían a una condición de carrera, invalidando por completo los resultados del primer remove
.
La única mejora que puedo ver es que mediante el uso final ConcurrentHashMap
y dejando synchronized
ya que son por lo menos podría evitar la copia innecesaria de datos.
¿Tiene sentido, o tal vez estoy pasando por alto algo aquí? ¿Alguien puede sugerir otras alternativas para esta solución?