为什么 Scala ConcurrentMap.putIfAbsent 总是尝试放置?
Why does Scala ConcurrentMap.putIfAbsent always try to put?
文档声明中的 ConcurrentMap 部分:
m putIfAbsent(k, v) Adds key/value binding k -> v unless k is already
defined in m
但实际上,我发现 putIfAbsent
总是试图插入 v
。解决这个问题的方法是使用 getOrElseUpdate
,但这真的是一个错误,还是我遗漏了什么?
我的代码如下:
val instanceMap: ConcurrentMap[Address, (MongodExecutable, MongodProcess)] =
(new JavaConcurrentMap[Address, (MongodExecutable, MongodProcess)]()).asScala
def start(host: String = DefaultAddress.host, port: Int = DefaultAddress.port): MongodProcess = {
val addr = Address(host, port)
instanceMap.putIfAbsent(addr, { // this block is ALWAYS executed
val mongodExecutable = starter.prepare(mongodConfig(host, port))
val mongod = mongodExecutable.start()
logProcessInfo(mongod)
(mongodExecutable, mongod)
})
instanceMap(addr)
._2
}
putIfAbsent
确实计算了 k, v
的值,因为值不是 function/lambda。
下面的例子
原并发图
scala> import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentHashMap
scala> new ConcurrentHashMap[String, String]()
res0: java.util.concurrent.ConcurrentHashMap[String,String] = {}
insert key1 - 插入成功returns null
scala> res0.putIfAbsent("127.0.0.1", "mongod-process1")
res1: String = null
scala> res0
res2: java.util.concurrent.ConcurrentHashMap[String,String] = {127.0.0.1=mongod-process1}
insert key2 - 插入成功returns null
scala> res0.putIfAbsent("127.0.0.2", "mongod-process2")
res3: String = null
scala> res0
res4: java.util.concurrent.ConcurrentHashMap[String,String] = {127.0.0.2=mongod-process2, 127.0.0.1=mongod-process1}
再次尝试插入key1 - 插入失败returnsvalue1
scala> res0.putIfAbsent("127.0.0.1", { println("evaluating mongod-process"); "mongod-process1-again" } )
evaluating mongod-process
res7: String = mongod-process1
最终地图
scala> res0
res8: java.util.concurrent.ConcurrentHashMap[String,String] = {127.0.0.2=mongod-process2, 127.0.0.1=mongod-process1}
此外,请注意 putIfAbsent
在开始时对键值进行空检查(ConcurrentHashMap.java:1011
),因此无论如何都必须评估该值。
scala> res0.putIfAbsent("127.0.0.1", { println("evaluating mongod-process"); null } )
evaluating mongod-process
java.lang.NullPointerException
at java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011)
at java.util.concurrent.ConcurrentHashMap.putIfAbsent(ConcurrentHashMap.java:1535)
... 29 elided
文档声明中的 ConcurrentMap 部分:
m putIfAbsent(k, v) Adds key/value binding k -> v unless k is already defined in m
但实际上,我发现 putIfAbsent
总是试图插入 v
。解决这个问题的方法是使用 getOrElseUpdate
,但这真的是一个错误,还是我遗漏了什么?
我的代码如下:
val instanceMap: ConcurrentMap[Address, (MongodExecutable, MongodProcess)] =
(new JavaConcurrentMap[Address, (MongodExecutable, MongodProcess)]()).asScala
def start(host: String = DefaultAddress.host, port: Int = DefaultAddress.port): MongodProcess = {
val addr = Address(host, port)
instanceMap.putIfAbsent(addr, { // this block is ALWAYS executed
val mongodExecutable = starter.prepare(mongodConfig(host, port))
val mongod = mongodExecutable.start()
logProcessInfo(mongod)
(mongodExecutable, mongod)
})
instanceMap(addr)
._2
}
putIfAbsent
确实计算了 k, v
的值,因为值不是 function/lambda。
下面的例子
原并发图
scala> import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentHashMap
scala> new ConcurrentHashMap[String, String]()
res0: java.util.concurrent.ConcurrentHashMap[String,String] = {}
insert key1 - 插入成功returns null
scala> res0.putIfAbsent("127.0.0.1", "mongod-process1")
res1: String = null
scala> res0
res2: java.util.concurrent.ConcurrentHashMap[String,String] = {127.0.0.1=mongod-process1}
insert key2 - 插入成功returns null
scala> res0.putIfAbsent("127.0.0.2", "mongod-process2")
res3: String = null
scala> res0
res4: java.util.concurrent.ConcurrentHashMap[String,String] = {127.0.0.2=mongod-process2, 127.0.0.1=mongod-process1}
再次尝试插入key1 - 插入失败returnsvalue1
scala> res0.putIfAbsent("127.0.0.1", { println("evaluating mongod-process"); "mongod-process1-again" } )
evaluating mongod-process
res7: String = mongod-process1
最终地图
scala> res0
res8: java.util.concurrent.ConcurrentHashMap[String,String] = {127.0.0.2=mongod-process2, 127.0.0.1=mongod-process1}
此外,请注意 putIfAbsent
在开始时对键值进行空检查(ConcurrentHashMap.java:1011
),因此无论如何都必须评估该值。
scala> res0.putIfAbsent("127.0.0.1", { println("evaluating mongod-process"); null } )
evaluating mongod-process
java.lang.NullPointerException
at java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011)
at java.util.concurrent.ConcurrentHashMap.putIfAbsent(ConcurrentHashMap.java:1535)
... 29 elided