0

putIfAbsent または短絡演算子のような同等のものを使用することは可能ですか?

myConcurrentMap.putIfAbsent(key,calculatedValue)

計算値が既に存在する場合は、再度計算しないでください。デフォルトでは、 putIfAbsent は、実際に値を再度保存することはありませんが、毎回計算を行います。

4

3 に答える 3

1

Future<V>オブジェクトをマップに配置できます。を使用putIfAbsentすると、オブジェクトは 1 つだけ存在し、最終的な値の計算は呼び出しによってFuture.get()(たとえばFutureTask +Callableクラスによって) 実行されます。この手法の使用に関する議論については、Java Concurrency in Practiceを参照してください。(コード例は、SOのこの質問にもあります。

このように、値は一度だけ計算され、すべてのスレッドが同じ値を取得します。map へのアクセスはブロックされませんが、( を介したFuture.get()) value へのアクセスは、この値がいずれかのスレッドによって計算されるまでブロックされます。

于 2011-06-09T09:53:24.093 に答える
1

Java では、組み込みのケースを除いて、いかなる形式のショートサーキットも許可されていません。悲しいことに、すべてのメソッド呼び出しは、制御がメソッドに渡される前に引数が完全に評価されることになります。したがって、「通常の」構文ではこれを行うことができません。などの計算を手動でラップしてからCallable、明示的に呼び出す必要があります。


ただし、この場合、とにかくどのように機能するかを理解するのは難しいと思います。 putIfAbsentアトミックなノンブロッキング操作に基づいて動作します。やりたいことをやるとしたら、一連のイベントはおおよそ次のようになります。

  1. マップに存在するかどうかを確認keyします (この例では存在しないことを前提としています)
  2. 評価calculatedValueする(質問のコンテキストを考えると、おそらく高価です)
  3. 結果を地図に入れる

ステップ 2 で値がまだ存在していない場合、これを非ブロッキングにすることは不可能です。このメソッドを同時に呼び出す 2 つの異なるスレッドは、ブロッキングが発生した場合にのみ正しく実行できます。この時点synchronizedで、必要な実装の柔軟性を備えたブロックを使用することもできます。次のような単純なロックを使用して、目的を確実に実装できます。

private final Map<K, V> map = ...;

public void myAdd(K key, Callable<V> valueComputation) {
    synchronized(map) {
        if (!map.containsKey(key)) {
            map.put(key, valueComputation.call());
        }
    }
}
于 2011-06-09T09:38:33.407 に答える
0

GuavaComputingMapの使用を検討できます

ConcurrentMap<Key, Value> myConcurrentMap = new MapMaker()
  .makeComputingMap(
    new Function<Key, Value>() {
      public Value apply(Key key) {
        Value calculatedValue = calculateValue(key);
        return calculatedValue;
      }
  });
于 2011-06-09T09:35:07.347 に答える