在54:15的his talk about Effective Java中,Joshua Bloch建议在使用putIfAbsent
之前使用get
,以提高性能和并发性。这就引出了一个问题,为什么这个优化没有像这样内置
public V BETTER_putIfAbsent(K key, V value) {
V result = get(key);
if (result!=null) return result;
return ORIGINAL_putIfAbsent(key, value);
}
发布于 2011-04-16 13:07:20
这添加了双重检查锁定,事务语义保持不变;因此它是正确的。
它是否真的是优化取决于使用情况。我们总是想检查那些我们知道有更便宜的解决方案的特殊情况。
if A
cheapSolutionForA();
else
genericSolution();
这可能会起作用,也可能不起作用-如果A
很少是true
,那么额外的检查成本就会超过节省的成本。(当A确实有时为真时,它可能会扰乱CPU分支预测,即使当A=true时,总是使用通用解决方案可能会更便宜)
在Joshua的例子中,A
确实经常是真的。他必须多次请求同一个字符串的实习生字符串(在值上相同,而不是在标识上),因此在大多数调用中,map已经具有键。
如果每次对intern()
的调用都得到一个不同的字符串,那么映射就永远不会有键,他的优化就会适得其反--“优化”会花费更多的时间,而且不会节省任何时间。
当然,当涉及到字符串实习生时,第一种情况在实践中更现实。
一般来说,putIfAbsent()
不能预测它是如何被使用的,所以在它里面包含这种特殊情况的“优化”是不明智的。在许多用例中,争用是低的,当调用putIfAbsent
时,映射很可能没有键,在这些情况下,如果“优化”是内置的,这将是错误的。
发布于 2011-04-16 12:59:49
我猜这是因为性能取决于使用模式。对于大多数putIfAbsent调用都会成功的map,使用get保护它会更慢。对于putIfAbsent经常失败的映射,使用get保护它会更快。
通过不对常见故障情况进行优化,您可以自由选择哪个方法更快。
在视频中的示例中,putIfAbsent通常会失败,因此使用get来保护它会更快。
https://stackoverflow.com/questions/5684252
复制相似问题