[英]Double checked locking with regular HashMap
回到并发。 到目前为止,很明显,对于double checked locking
,变量需要声明为volatile
。 但是,如果使用双重检查锁定,如下所示。
class Test<A, B> {
private final Map<A, B> map = new HashMap<>();
public B fetch(A key, Function<A, B> loader) {
B value = map.get(key);
if (value == null) {
synchronized (this) {
value = map.get(key);
if (value == null) {
value = loader.apply(key);
map.put(key, value);
}
}
}
return value;
}
}
为什么它必须是ConcurrentHashMap而不是常规的HashMap ? 所有映射修改都在synchronized
块中完成,代码不使用迭代器,因此从技术上讲,应该没有“并发修改”问题。
请不要建议使用putIfAbsent
/ computeIfAbsent
因为我在询问概念而不是使用API :)除非使用此API有助于HashMap
与ConcurrentHashMap
主题。
更新2016-12-30
Holger的评论回答了这个问题“ HashMap.get
没有修改结构,但你的put
调用确实如此。因为在调用synchronized块之外调用get
,它可以看到put
的不完整状态行动同时发生。“ 谢谢!
这个问题混淆了很多,很难回答。
如果只从一个线程调用此代码,那么你就太复杂了; 你不需要任何同步。 但显然这不是你的意图。
因此,多个线程将调用fetch方法,该方法在没有任何同步的情况下委托给HashMap.get()。 HashMap不是线程安全的。 巴姆,故事的结尾。 如果您正在尝试模拟双重检查锁定,则无关紧要; 实际情况是在地图上调用get()
和put()
将操纵HashMap
的内部可变数据结构,而不会在所有代码路径上进行一致同步,并且因为您可以从多个线程同时调用这些,所以您已经死。
(另外,您可能认为HashMap.get()
是一个纯读操作,但这也是错误的。如果HashMap实际上是一个LinkedHashMap(它是HashMap的子类)怎么办.BlinkHashMap.get()会更新访问顺序这涉及到内部数据结构的写入 - 这里同时没有同步。但是即使get()没有写入,你的代码仍然会被破坏。)
经验法则:当你认为你有一个巧妙的技巧可以让你避免同步时,你几乎肯定是错的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.