我有这段代码
private Templates retrieveFromCache(String name) {
TemplatesWrapper t = xlCache.get(name);
synchronized(t){
if (!t.isValid()) {
xlCache.remove(name);
return null;
}
}
return t.getTemplate();
}
xlCache
是ConcurrentHashMap
;我在t
上进行同步的原因是2个线程可以交错,当线程1验证谓词时,线程2已经从地图中删除了对象,然后抛出了NullPointerException
。 我的假设是正确的,因为我知道并发性是推理的难度之一。然后到我原来的问题,我可以锁定t
,即使它是本地的吗?
这也是private
方法,它从public
方法调用,是否会产生差异?
编辑:由于NullPointerException
返回remove()
使同步失去意义,因此抛出boolean
的原始前提不正确;但是,我的问题是锁定了一个本地对象。
答案 0 :(得分:2)
ConcurrentHashMap
(和Map
/ ConcurrentMap
一般)不会抛出异常。这就是remove
方法返回boolean
的原因,以指示是否实际删除了 。
但是,你可以锁定局部变量。毕竟,您实际上是通过引用(以及与引用对象关联的监视器)锁定的,而不是变量 - 而另一个并发运行的方法将具有相同的引用。
答案 1 :(得分:2)
您可以锁定所需的任何对象。但是,在您的情况下,看起来您可以更清晰,更安全地解决它。
同步应尽可能本地化。由于您从某个未知位置获取TemplatesWrapper
,因此任何人都可以在其上进行同步,这使得很难控制并发性。通过查看代码被锁定的原因,它也应该尽可能明显。
最好将同步放在xlCache中,例如removeIfInvalid()
答案 2 :(得分:1)
是的,这样可以正常工作。
您可以在java中的任何对象上进行同步,这样您的代码就可以运行并且是线程安全的。
因为你没有检查是否为空,所以公寓。我猜你刚刚错过了你的代码示例?
答案 3 :(得分:1)
更好的方法是使用ConcurrentMap中的2 arg remove方法(假设t具有合理的equals实现)。那么你不需要任何同步:
private Templates retrieveFromCache(String name) {
TemplatesWrapper t = xlCache.get(name);
if (!t.isValid()) {
xlCache.remove(name, t);
return null;
}
return t.getTemplate();
}
答案 4 :(得分:0)
如果remove(null)会调用空指针异常,这似乎是合理的。如果你不认为碰撞是一个常见的问题,你也可以实现一个可能更快的代码版本,只需将try / catch包裹起来而不是同步。
在任何一种情况下,我都会在那里添加评论来解释你为什么做了你所做的事情,所以从现在开始一个月,它仍然有意义。