一意のキーを持ちますが、重複する値を含めることができるキャッシュのやや具体的な実装を記述する必要があります。たとえば、次のようになります。
"/path/to/one" -> 1
"/path/to/two" -> 2
"/path/to/vienas" -> 1
"/path/to/du" -> 2
このクラスは、ノンブロッキングの読み取り/キー ルックアップを提供する必要がありますが、典型的な作成/更新/削除ミューテーターも備えています。たとえば、値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
の使用法をwithに置き換えてfinal ConcurrentHashMap
すべてのブロックを削除するとsynchronized
、競合する同時ミューテーターが互いに無効になる可能性はありますか? たとえば、 の 2 つの同時呼び出しがremove
競合状態につながり、最初の の結果が完全に無効になることを想像できremove
ます。
私が見ることができる唯一の改善点は、使用してfinal ConcurrentHashMap
そのままsynchronized
にしておくことで、少なくともデータの不要なコピーを回避できることです。
それは理にかなっていますか? それとも、ここで何かを見落としているのでしょうか? このソリューションの他の代替案を提案できる人はいますか?