回到并发。到目前为止,很明显,要使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
块内完成,代码不使用迭代器,因此从技术上讲,不应该有&#34;并发修改&#34;问题。
请避免建议使用putIfAbsent
/ computeIfAbsent
因为我询问概念而不是API的使用:)除非使用此API有助于{{ 1}} vs HashMap
主题。
更新2016-12-30
Holger&#34; ConcurrentHashMap
下面的评论回答了这个问题。HashMap.get
不修改结构,但是你put
的调用确实如此。由于在同步块之外调用了get
,因此可以看到同时发生put
操作的不完整状态。&#34;谢谢!
答案 0 :(得分:14)
这个问题混淆了很多,很难回答。
如果只从单个线程调用此代码,那么您将使其过于复杂;你不需要任何同步。但很明显,这不是你的意图。
因此,多个线程将调用fetch方法,该方法在没有任何同步的情况下委托给HashMap.get()。 HashMap不是线程安全的。巴姆,故事的结尾。如果你试图模拟双重检查锁定,那就不重要了;现实情况是,在地图上调用get()
和put()
将操纵HashMap
的内部可变数据结构,而不会在所有代码路径上进行一致同步,因为您可以同时调用这些从多个线程,你已经死了。
(另外,您可能认为HashMap.get()
是一个纯粹的读取操作,但也是错误的。如果HashMap实际上是一个LinkedHashMap(它是HashMap的子类,那该怎么办。)LinkedHashMap.get ()将更新访问顺序,这涉及写入内部数据结构 - 这里,同时没有同步。但即使get()没有写入,这里的代码仍然被破坏。)