5

ロジックをセンスチェックするための並行マップの特定の使用法について話し合いたいと思いました...

を使用した場合ConcurrentHashMap、ファミリアを実行できます

private final ConcurrentHashMap<K, V> map = new ConcurrentHashMap<K, V>();

public V getExampleOne(K key) {
    map.putIfAbsent(key, new Object());
    return map.get(key);
}

しかし、 と の間のマップからアイテムを削除すると、上記のメソッドがコレクションに存在しなくなったものを返すという競合状態が存在することに気付きました。これはうまくいくかもしれないし、うまくいかないかもしれませんが、私のユースケースではうまくいかないと仮定しましょう。putIfAbsentget

私が本当に望んでいるのは、すべてをアトミックにすることです。そう、

public V getExampleTwo(K key) {
    return map.putIfAbsent(key, new Object());
}

しかし、これが展開するにつれて

if (!map.containsKey(key))
   return map.put(key, value);     [1]
return map.get(key);

これは、行 [1] がnull最初に使用されたときに返されます (つまり、map.put以前の値が返され、最初に使用された場合は になりますnull)。

このインスタンスで null を返すことはできません

それは私に次のようなものを残します;

public V getExampleThree(K key) {
    Object object = new Object();
    V value = locks.putIfAbsent(key, object);
    if (value == null)
        return object;
    return value;
}

最後に、私の質問です。上記の例はセマンティクスでどのように異なりますか?. getExampleThreeアトミック性を保証しgetExampleTwoますが、null リターンを正しく回避しますか? 他に問題はありgetExampleThreeますか?

選択について少し議論したいと思っていました。ConcurrentHashMapメソッドを呼び出すクライアントとgetマップから削除するメソッドを使用して非同期を使用できることを認識していますが、それは ConcurrentHashMap の目的 (非ブロッキングの性質) を無効にしているようです。データを正確に保つための唯一の選択肢ですか?

それが、ConcurrentHashMap を選択する理由の一部だと思います。操作した時点で表示/最新/正確であることを確認しますが、古いデータが問題になる場合は、さらに先に影響がある可能性があります...

4

4 に答える 4

5

キーのグローバルロックオブジェクトを作成しようとしているようです。

エントリを削除してほぼすぐに再作成する可能性があるのではなく、エントリが二度と必要ないと確信できる場合にのみ、エントリを削除します。

それ以外の場合、これをロックに使用している場合は、同じキーの異なるオブジェクトに2つのスレッドをロックすることができます。


問題がない場合は、ビジーループでループできます。

public V getExampleOne(K key) {
    for(Object o = null, ret = null; (ret = map.get(key)) == null; )
        map.putIfAbsent(key, o == null ? o = new Object() : o);
    return ret;
}

ループが存在するとすぐに削除または交換できるため、実質的にはとほぼ同じです。

public V getExampleThree(K key) {
    Object o = new Object();
    map.putIfAbsent(key, o);
    Object ret = map.get(key);
    return ret == null ? o : ret;
}

それで、最後に、私の質問です。上記の例はセマンティクスがどのように異なりますか?

違いは明らかです。

getExampleThreeはgetExampleTwoのような原子性を保証しますが、nullが正しく返されるのを避けますか?

はい。

getExampleThreeに他の問題はありますか?

次の呼び出しで別の値が得られないと思われる場合のみ(別のスレッドで削除できると思われる場合)

于 2012-01-10T08:52:29.203 に答える
3

メソッドには異なるセマンティクスがあります。

  • getExampleOne はアトミックではありません。
  • 新しいオブジェクトがマップに挿入された場合、getExampleTwo は null を返します。これは getExampleOne の動作とは異なりますが、アトミックです。
  • getExampleThree はおそらくあなたが望むものです。これはアトミックであり、putIfAbsent 呼び出しの時点の後にマップ内にあるオブジェクトを返します。ただし、null がアプリケーションで有効な値である場合は問題でした。その場合、null の戻り値はあいまいです。

ただし、状況によっては、戻り値を使用した時点で実際のオブジェクトではない場合があります。次に、明示的なロックが必要です。

于 2012-01-10T08:45:27.053 に答える
0

秘訣は、アトミックではないと想定して処理することだと思います。

私はあなたが何を探しているのか本当にはっきりしていません。これが接線でオフになっている場合はお知らせください。変更します。

おそらくあなたは次のようなものを探しています:

private final ConcurrentHashMap<String, Object> map = new ConcurrentHashMap();

/*
 * Guaranteed to return the object replaced.
 * 
 * Note that by the time this method returns another thread 
 * may have already replaced the object again.
 * 
 * All we can guarantee here is that the return value WAS 
 * associated with the key in the map at the time the entry was 
 * replaced.
 * 
 * A null return implies that either a null object was associated
 * with the specified key or there was no entry in the map for 
 * the specified key wih 'null' as it's 'value'.
 */
public Object consistentReplace ( String key, Object newValue ) {
  Object oldValue = map.get(key);
  while ( !map.replace(key, oldValue, newValue) ) {
    // Failed to replace because someone got in there before me.
    oldValue = map.get(key);
  }
  return oldValue;
}
于 2012-01-12T19:48:32.027 に答える
0

単純に最初のバージョンを使用してメソッドを同期しないのはなぜですか?

public synchronized V getExampleOne(K key) {
    map.putIfAbsent(key, new Object());
    return map.get(key);
}

最大の並列処理を提供するわけではありませんが、操作が 2 つしかないことも事実でありgetExampleThree、正しいとはいえ、コードを読んでいる他の人にとっては読みにくく、理解しにくいものです。

于 2012-01-10T10:48:57.803 に答える