ConcurrentHashMap
を使用するコードをいくつか持ち、私が行っていることが正しいかどうかを確認したいと思います。ConcurrentHashMap:コレクションを値として保存するときの同期の必要性
マップには文字列をキーとして、コレクションには値(Set
)を格納します。私は当初、原子的にいくつかの操作(put、removeなど)を行うためのロックストライピングパターンを使用する実装を持っていましたが、その代わりにcomputeIfAbsent/putIfAbsent
(put用)とcomputeIfAbsent
(remove用)を使用できることを認識しました。複数のスレッドが4つのメソッド(get、put、removeKey、removeValue)をすべて呼び出します。
私のAFTERコードが正しいのか、スレッドセーフであるのかは誰にでも分かりますか?前もって感謝します!
private final ConcurrentMap<K, Set<V>> map = new ConcurrentHashMap<>();
private final ConcurrentMap<K, Object> locks = new ConcurrentHashMap<>();
public Set<V> getValue(K key) {
if (map.get(key) == null) return null;
return map.get(key).stream().map(k -> k.getValue()).collect(Collectors.toSet());
}
private void putValue(K key, V value) {
Object lock = locks.putIfAbsent(key, new Object());
if (lock == null) lock = locks.get(key);
Set<V> existingSet = map.computeIfAbsent(key, k -> {
Set<V> values = new ConcurrentSkipListSet<>(MY_COMPARATOR);
values.add(value);
return values;
});
if (existingSet != null) { // <- my guess is that this is unnecessary
synchronized (lock) {
map.get(key).add(expiringValue);
}
}
}
public void removeKey(K key) {
if (key == null) return;
if (map.get(key) != null) {
Object lock = locks.get(key);
synchronized (lock) {
map.remove(key);
}
locks.remove(key);
}
}
public void removeValue(K key, V value) {
if (key == null || value == null) return;
if (map.get(key) != null) {
Object lock = locks.get(key);
synchronized (lock) {
map.get(key).remove(value);
if (map.get(key).size() == 0) {
map.remove(key);
}
}
}
}
private void putValue(K key, V value) {
map.computeIfAbsent(key, k -> new ConcurrentSkipListSet<>(MY_COMPARATOR)).add(value);
}
public void removeKey(K key) {
map.remove(key);
}
public void removeValue(K key, V value) {
Set<ExpiringValue<V>> resultingValues = map.computeIfPresent(key, (k, values) -> {
values.remove(value);
return values;
});
if (resultingValues != null && resultingValues.size() == 0) {
map.remove(key);
}
}
コレクション値を初期化するために 'computeIfAbsent'を使用するのは、まだ存在しない場合です。 – Antoniossss