如何获得 TrieMap.getOrElseUpdate 的真正原子更新
How to get truly atomic update for TrieMap.getOrElseUpdate
据我了解,TrieMap.getOrElseUpdate
仍然不是真正的原子,并且 this 仅修复 returned 结果(在此之前它可能 return 不同调用者的不同实例修复),因此更新程序函数仍可能被调用多次,如文档(2.11.7)所述:
Note: This method will invoke op at most once. However, op may be invoked without the result being added to the map if a concurrent process is also trying to add a value corresponding to the same key k.
*我已经手动检查了 2.11.7,仍然 "at least once"
如何保证一次性调用(如果我对工厂使用TrieMap)?
我认为这个解决方案应该可以满足我的要求:
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
其实里面用的是synchronized。而且 get 里面的代码应该是
不过,性能可能会在未来得到改善:SIP-20
测试:
scala> (0 to 10000000).par.map(_ => getSingleton("zzz")).last
res8: Int = 1
P.S。 Java 在 ConcurrentHashMap
上有 computeIfAbscent
方法,我也可以使用它。
据我了解,TrieMap.getOrElseUpdate
仍然不是真正的原子,并且 this 仅修复 returned 结果(在此之前它可能 return 不同调用者的不同实例修复),因此更新程序函数仍可能被调用多次,如文档(2.11.7)所述:
Note: This method will invoke op at most once. However, op may be invoked without the result being added to the map if a concurrent process is also trying to add a value corresponding to the same key k.
*我已经手动检查了 2.11.7,仍然 "at least once"
如何保证一次性调用(如果我对工厂使用TrieMap)?
我认为这个解决方案应该可以满足我的要求:
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
其实里面用的是synchronized。而且 get 里面的代码应该是
不过,性能可能会在未来得到改善:SIP-20
测试:
scala> (0 to 10000000).par.map(_ => getSingleton("zzz")).last
res8: Int = 1
P.S。 Java 在 ConcurrentHashMap
上有 computeIfAbscent
方法,我也可以使用它。