如何获得 TrieMap.getOrElseUpdate 的真正原子更新

Posted

技术标签:

【中文标题】如何获得 TrieMap.getOrElseUpdate 的真正原子更新【英文标题】:How to get truly atomic update for TrieMap.getOrElseUpdate 【发布时间】:2016-02-18 14:59:22 【问题描述】:

据我了解,TrieMap.getOrElseUpdate 仍然不是真正的原子,this 仅修复返回的结果(在此修复之前它可能会为不同的调用者返回不同的实例),因此更新函数仍然可能被调用多次,如文档(针对 2.11.7)说:

注意:此方法最多调用一次 op。但是,如果并发进程也尝试添加与同一键 k 对应的值,则可能会调用 op 而不会将结果添加到映射中。

*我已经手动检查了 2.11.7,仍然“至少一次”

如何保证一次性调用(如果我使用 TrieMap 做工厂)?

【问题讨论】:

【参考方案1】:

我认为这个解决方案应该可以满足我的要求:

trait LazyComp  val get: Int 

val map = new TrieMap[String, LazyComp]()

val count = new AtomicInteger() //just for test, you don't need it

def getSingleton(key: String) = 
  val v = new LazyComp 
    lazy val get = 
      //compute something
      count.incrementAndGet() //just for test, you don't need it
    
  
  map.putIfAbsent(key, v).getOrElse(v).get

我相信,lazy val 实际上在里面使用了同步。而且get里面的代码应该是safe from exceptions

不过,未来可能会提高性能:SIP-20

测试:

scala> (0 to 10000000).par.map(_ => getSingleton("zzz")).last
res8: Int = 1

附: Java 在ConcurrentHashMap 上有computeIfAbscent 方法,我也可以使用它。

【讨论】:

为什么不用lazy val v = ... 它不起作用。如果你运行我的测试 - 你会得到不止一个(我有 3 个) @JeanLogeart 那是因为 putIfAbsent 的第二个参数不是惰性的 @JeanLogeart getOrElseUpdate 的同样问题。不管它有更新程序的名称调用参数 - 它仍然会在不适当的时间调用它。

以上是关于如何获得 TrieMap.getOrElseUpdate 的真正原子更新的主要内容,如果未能解决你的问题,请参考以下文章

求教,如何获得comboBox中当前选择的值

如何获得物体度数?

如何在一行上获得 3d 瓷砖并在悬停时获得缩放效果?

PHP如何获得POST传来的值?

CSDN如何获取C币

SQl中如何获得时间的时,分,秒部分?