0

是否可以使用 putIfAbsent 或其任何等效项,例如短路运算符。

myConcurrentMap.putIfAbsent(key,calculatedValue)

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

4

3 回答 3

1

Java不允许任何形式的短路保存内置案例,遗憾的是 - 所有方法调用都会导致在控制传递给方法之前对参数进行完全评估。因此,您无法使用“正常”语法来做到这一点;您需要手动将计算包含在 aCallable或类似文件中,然后显式调用它。


不过,在这种情况下,我发现很难看出它是如何工作的。 putIfAbsent基于原子的、非阻塞操作的基础上工作。如果要按照您的意愿行事,则事件的顺序大致是:

  1. 检查key地图中是否存在(此示例假设不存在)
  2. 评估calculatedValue(考虑到问题的背景,可能很昂贵)
  3. 将结果放入地图

如果在第二步中该值不存在,那么它就不可能是非阻塞的 - 同时调用此方法的两个不同线程只有在发生阻塞时才能正确执行。此时,您不妨只使用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 回答
1

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

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

于 2011-06-09T09:53:24.093 回答
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;
      }
  });
于 2011-06-09T09:35:07.347 回答