Ho bisogno di scrivere un po 'specifica implementazione di una cache, che ha chiavi univoche ma può contenere valori duplicati, ad esempio:utilizzo multithread di `iteratori ConcurrentHashMap`
"/path/to/one" -> 1
"/path/to/two" -> 2
"/path/to/vienas" -> 1
"/path/to/du" -> 2
La classe ha bisogno di offrire non bloccante legge/ricerche chiave, ma ha anche i tipici mutatori create/update/delete. Ad esempio, la rimozione di valore 2
dovrebbe portare
"/path/to/one" -> 1
"/path/to/vienas" -> 1
Legge per questa cache si superano le scritture di gran lunga in modo da scrivere le prestazioni non è un problema - fino a quando le scritture concorrenti non funzionano su uno sopra l'altro. Il numero complessivo di voci è molto probabile che sarà inferiore a 1000, quindi la ripetizione occasionale dei valori è ancora accessibile.
Così ho scritto qualcosa di simile (pseudo codice):
//
// 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);
}
}
Dopo aver scritto quanto sopra, ho capito che ConcurrentHashMap
offre anche non bloccante legge che renderebbe tutto il mio sforzo inutile, ma c'è una dichiarazione in la sua Javadoc che alza un sopracciglio:
iterators are designed to be used by only one thread at a time
Quindi, se sostituisco l'utilizzo di volatile ImmutableMap
con final ConcurrentHashMap
e rimuovere tutti i blocchi synchronized
, è possibile che in competizione mutatori concorrenti saranno invalido l'un l'altro? Ad esempio, posso immaginare come due chiamate simultanee a remove
comporterebbero una condizione di competizione, invalidando completamente i risultati del primo remove
.
L'unico miglioramento che posso vedere è che utilizzando final ConcurrentHashMap
e lasciando synchronized
come sono ho potuto almeno evitare di copiare inutile di dati.
Ha senso - o forse sto trascurando qualcosa qui? Qualcuno può suggerire altre alternative per questa soluzione?