ConcurrentHashMap.putIfAbsent的性能

maa*_*nus 5 java concurrenthashmap

在54:15 关于Effective Java的讨论中,Joshua Bloch建议get之前使用它putIfAbsent来提高性能和并发性.这引出了一个问题,为什么这个优化已经没有像

public V BETTER_putIfAbsent(K key, V value) {
    V result = get(key);
    if (result!=null) return result;
    return ORIGINAL_putIfAbsent(key, value);
}
Run Code Online (Sandbox Code Playgroud)

irr*_*ble 1

这增加了双重检查锁定,事务语义保持不变;所以这并没有错

是否真的是优化取决于使用情况。我们总是想检查是否存在我们知道存在更便宜的解决方案的特殊情况

if A
    cheapSolutionForA();
else
    genericSolution();
Run Code Online (Sandbox Code Playgroud)

这可能有效,也可能无效——如果A很少发生true,则额外检查的成本将超过其节省的成本。(当 A 有时确实为真时,它可能会破坏 CPU 分支预测,即使 A=true 时,始终采用通用解决方案可能会更便宜)

在约书亚的例子中,A确实经常如此。他必须多次请求相同字符串的实习生字符串(值相同,而不是身份相同),因此在大多数调用中,映射已经拥有密钥。

如果每次调用都intern()得到不同的字符串,那么映射永远不会有密钥,并且他的优化会适得其反——“优化”会花费更多时间,并且不会节省任何时间。

当然,说到string intern,实践中第一种情况更现实。

但总的来说,putIfAbsent()无法预测它的使用方式,因此在其中包含这种特殊情况的“优化”是不明智的。在许多用例中,争用很低,映射在调用时很可能没有密钥putIfAbsent,如果内置“优化”,在这些情况下将是错误的。