开发者

Using putIfAbsent like a short circuit operator

开发者 https://www.devze.com 2023-03-11 06:50 出处:网络
Is it possible to use putIfAbsent or any of its equivalents like a short circuit operator. myConcurrentMap.putIfAbsent(key,calculatedValue)

Is it possible to use putIfAbsent or any of its equivalents like a short circuit operator.

myConcurrentMap.putIfAbsent(key,calculatedValue)

I want that if there is开发者_StackOverflow中文版 already a calculatedValue it shouldnt be calculated again. by default putIfAbsent would still do the calculation every time even though it will not actually store the value again.


Java doesn't allow any form of short-circuiting save the built-in cases, sadly - all method calls result in the arguments being fully evaluated before control passes to the method. Thus you couldn't do this with "normal" syntax; you'd need to manually wrap up the calculation inside a Callable or similar, and then explicitly invoke it.


In this case I find it difficult to see how it could work anyway, though. putIfAbsent works on the basis of being an atomic, non-blocking operation. If it were to do what you want, the sequence of events would roughly be:

  1. Check if key exists in the map (this example assumes it doesn't)
  2. Evaluate calculatedValue (probably expensive, given the context of the question)
  3. Put result in map

It would be impossible for this to be non-blocking if the value didn't already exist at step two - two different threads calling this method at the same time could only perform correctly if blocking happened. At this point you may as well just use synchronized blocks with the flexibility of implementation that that entails; you can definitely implement what you're after with some simple locking, something like the following:

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());
        }
    }
}


You can put Future<V> objects into the map. Using putIfAbsent, only one object will be there, and computation of final value will be performed by calling Future.get() (e.g. by FutureTask + Callable classes). Check out Java Concurrency in Practice for discussion about using this technique. (Example code is also in this question here on SO.

This way, your value is computed only once, and all threads get same value. Access to map isn't blocked, although access to value (through Future.get()) will block until this value is computed by one of the threads.


You could consider to use a Guava ComputingMap

ConcurrentMap<Key, Value> myConcurrentMap = new MapMaker()
  .makeComputingMap(
    new Function<Key, Value>() {
      public Value apply(Key key) {
        Value calculatedValue = calculateValue(key);
        return calculatedValue;
      }
  });
0

精彩评论

暂无评论...
验证码 换一张
取 消