putIfAbsent または短絡演算子のような同等のものを使用することは可能ですか?
myConcurrentMap.putIfAbsent(key,calculatedValue)
計算値が既に存在する場合は、再度計算しないでください。デフォルトでは、 putIfAbsent は、実際に値を再度保存することはありませんが、毎回計算を行います。
putIfAbsent または短絡演算子のような同等のものを使用することは可能ですか?
myConcurrentMap.putIfAbsent(key,calculatedValue)
計算値が既に存在する場合は、再度計算しないでください。デフォルトでは、 putIfAbsent は、実際に値を再度保存することはありませんが、毎回計算を行います。
Future<V>
オブジェクトをマップに配置できます。を使用putIfAbsent
すると、オブジェクトは 1 つだけ存在し、最終的な値の計算は呼び出しによってFuture.get()
(たとえばFutureTask
+Callable
クラスによって) 実行されます。この手法の使用に関する議論については、Java Concurrency in Practiceを参照してください。(コード例は、SOのこの質問にもあります。
このように、値は一度だけ計算され、すべてのスレッドが同じ値を取得します。map へのアクセスはブロックされませんが、( を介したFuture.get()
) value へのアクセスは、この値がいずれかのスレッドによって計算されるまでブロックされます。
Java では、組み込みのケースを除いて、いかなる形式のショートサーキットも許可されていません。悲しいことに、すべてのメソッド呼び出しは、制御がメソッドに渡される前に引数が完全に評価されることになります。したがって、「通常の」構文ではこれを行うことができません。などの計算を手動でラップしてからCallable
、明示的に呼び出す必要があります。
ただし、この場合、とにかくどのように機能するかを理解するのは難しいと思います。 putIfAbsent
アトミックなノンブロッキング操作に基づいて動作します。やりたいことをやるとしたら、一連のイベントはおおよそ次のようになります。
key
します (この例では存在しないことを前提としています)calculatedValue
する(質問のコンテキストを考えると、おそらく高価です)ステップ 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());
}
}
}
GuavaComputingMapの使用を検討できます
ConcurrentMap<Key, Value> myConcurrentMap = new MapMaker()
.makeComputingMap(
new Function<Key, Value>() {
public Value apply(Key key) {
Value calculatedValue = calculateValue(key);
return calculatedValue;
}
});