【发布时间】:2016-02-21 10:22:07
【问题描述】:
关于 SO 的一些答案提到,如果未正确同步(通常底线是“不要使用 HashMap在多线程环境中,使用 ConcurrentHashMap")。
虽然我可以很容易地看出为什么对 HashMap.put(Object) 方法的并发调用会导致无限循环,但我不太明白为什么 get(Object) 方法在尝试读取 HashMap 时会卡住在那一刻正在调整大小。我查看了implementation in openjdk,它包含一个循环,但退出条件e != null 迟早会实现。怎么可能永远循环?
明确提到易受此问题影响的一段代码是:
public class MyCache {
private Map<String,Object> map = new HashMap<String,Object>();
public synchronized void put(String key, Object value){
map.put(key,value);
}
public Object get(String key){
// can cause in an infinite loop in some JDKs!!
return map.get(key);
}
}
有人可以解释一个线程如何将一个对象放入 HashMap 和另一个读取它的线程可以以这样一种方式交错以生成无限循环吗?是否与某些缓存一致性问题或 CPU 指令重新排序有关(所以问题只能发生在多处理器机器上)?
【问题讨论】:
-
你真的可以编译它并让它永远运行吗?似乎异常会比无限循环抛出更多
-
为什么不使用
AtomicReference“锁定”您的地图?您将得到其余的非线程安全问题。 -
这个练习毫无意义。 HashMap 不是线程安全的,并且在另一个线程写入时从中取出对象,即使它从未进入无限循环,也可能返回错误结果、破坏 HashMap、引发异常或其他任何事情。你为什么要让这一切发生?只需同步 get 方法:有必要使代码线程安全。
-
@DavidHaim 我没有尝试运行它:我很想知道一个线程对“put”的调用和另一个线程对“get”的同时调用是否属实可以导致无限循环,如果是真的,我想知道它是怎么可能的:例如两个线程中的指令必须如何交错?这会发生在什么架构上?通过查看 openjdk 中的实现,我看不出如何以可能导致无限循环的方式将此 Java 代码转换为机器指令。谁能解释一下?
-
@JBNizet 这不是“练习”,而是一个精确的问题:在某些架构上的某些 jdk 实现下,对“get”方法的调用是否会陷入无限循环,或者这是不可能的?我提到的答案声称这可能发生,但没有解释如何发生。我很清楚,当共享对象可以被另一个线程修改时,不应该读取它(这意味着:始终同步对可修改共享对象的访问,或者使它们成为原子的)。我只是想了解为什么人们声称这会导致无限循环,为什么没有意义?理解永远没有意义。
标签: java multithreading concurrency hashmap