使用putIfAbsent就像一个短路运算符

时间:2011-06-09 09:18:29

标签: java concurrenthashmap

是否可以使用putIfAbsent或其任何等价物,如短路操作符。

myConcurrentMap.putIfAbsent(key,calculatedValue)

我希望如果已经有一个calculatedValue,则不应再次计算。 默认情况下,putIfAbsent仍然会每次进行计算,即使它实际上不会再次存储该值。

3 个答案:

答案 0 :(得分:1)

Java不允许任何形式的短路保存内置案例,遗憾的是 - 所有方法调用都会导致在控制传递给方法之前完全评估参数。因此,你不能用“普通”语法做到这一点;你需要在Callable或类似的内部手动包装计算,然后显式调用它。


在这种情况下,我发现很难看出它是如何工作的。 putIfAbsent基于原子非阻塞操作而工作。如果要做你想做的事情,事件的顺序大致是:

  1. 检查地图中是否存在key(此示例假定它不存在)
  2. 评估calculatedValue(考虑到问题的背景可能很昂贵)
  3. 将结果放入地图
  4. 如果在步骤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());
            }
        }
    }
    

答案 1 :(得分:1)

您可以将Future<V>个对象放入地图中。使用putIfAbsent,只有一个对象,并且通过调用Future.get()(例如,通过FutureTask + Callable类)来执行最终值的计算。有关使用此技术的讨论,请查看Java Concurrency in Practice。 (示例代码也在SO {。{3}}中。

这样,您的值只计算一次,并且所有线程都获得相同的值。虽然访问值(通过Future.get())将阻塞,直到该值由其中一个线程计算,但不会阻止对映射的访问。

答案 2 :(得分:0)

您可以考虑使用Guava ComputingMap

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