Java 为什么在同步更改操作时需要同步HashMap.get(key)?
我在一个HashMap上使用来自多个线程的Java 为什么在同步更改操作时需要同步HashMap.get(key)?,java,multithreading,synchronization,Java,Multithreading,Synchronization,我在一个HashMap上使用来自多个线程的.get(…)、.put(…)和.clear()操作.put(…)和.clear()在已同步的块中,但.get(…)不是。我无法想象这会导致问题,但在我看到的其他代码中,.get()几乎总是同步的 get/put的相关代码 Object value = map.get(key); if(value == null) { synchronized (map) { value = map.get(key); // check again, mig
.get(…)
、.put(…)
和.clear()
操作.put(…)
和.clear()
在已同步的块中,但.get(…)
不是。我无法想象这会导致问题,但在我看到的其他代码中,.get()
几乎总是同步的
get/put的相关代码
Object value = map.get(key);
if(value == null) {
synchronized (map) {
value = map.get(key); // check again, might have been changed in between
if(value == null) {
map.put(key, new Value(...));
}
}
}
清楚的是:
synchronized (map) {
map.clear();
}
写入操作将使缓存无效,因为同步操作和get(…)
返回null或实例。通过将.get(…)
操作放入synchronized(map)
块中,我真的看不出会出现什么问题或会有什么改进。下面是一个简单的场景,它会在非同步get
时产生问题:
- 线程A启动
get
,计算散列存储桶编号,并被抢占
- 线程B调用
clear()
,因此会分配一个较小的存储桶数组
- 线程A唤醒,可能会遇到索引越界异常
下面是一个更复杂的场景:
- 线程A锁定地图进行更新,并被抢占
- 线程B启动一个
get
操作,计算散列存储桶编号,并被抢占
- 线程A醒来,继续执行
放置
,并意识到存储桶需要调整大小
- 线程A分配新的存储桶,将旧内容复制到存储桶中,并添加新项
- 线程B唤醒,并使用新存储桶数组上的旧存储桶索引继续搜索
此时,A可能无法找到正确的项,因为它很可能位于不同索引的哈希桶中。这就是为什么get
也需要同步的原因。上述所有操作都可以在程序的顺序一致执行中发生,但Java甚至不能保证SC。因此,即使要以某种方式避免这里描述的所有问题,get
仍需同步。这就是创建java.util.concurrent.locks.ReadWriteLock的原因。