如何获得 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 的真正原子更新的主要内容,如果未能解决你的问题,请参考以下文章