使用常规HashMap双重检查锁定
回到并发。 到目前为止,很明显,对于double checked locking
,变量需要声明为volatile
。 但是,如果使用双重检查锁定,如下所示。
class Test { private final Map map = new HashMap(); public B fetch(A key, Function 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
块中完成,代码不使用迭代器,因此从技术上讲,应该没有“并发修改”问题。
因为我在询问概念而不是API的使用,请避免建议使用putIfAbsent
/ computeIfAbsent
,除非使用此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的子类)会怎样。LinkedHashMap.get()会更新访问顺序这涉及写入内部数据结构 – 这里,同时没有同步。但即使get()没有写入,这里的代码仍然被破坏。)
经验法则:当你认为你有一个巧妙的技巧可以让你避免同步时,你几乎肯定是错的。
- Java ConcurrentHashMap不是线程安全的..是吗?
- 并发hashmap不需要同步的getter / setter吗?
- ConcurrentHashMap是否可能“死锁”?
- “在Java 8中构建时,未定义的引用:.. ConcurrentHashMap.keySet()”
- ConcurrentHashMap返回一个弱一致的迭代器,为什么我们应该使用它呢?
- ThreadLocal HashMap vs ConcurrentHashMap用于线程安全的未绑定缓存
- ConcurrentHashMap jdk 8使用TreeNodes而不是List ..为什么?
- 同步局部变量
- 需要简单解释“锁定条带化”如何与ConcurrentHashMap一起使用