为什么同步变更操作的时候需要同步HashMap.get(key)?
Why does HashMap.get(key) needs to be synchronized when change operations are synchronized?
我在一个 HashMap 上使用来自多个线程的 .get(...)
、.put(...)
和 .clear()
操作。 .put(...)
和 .clear()
在 synchronized
块内,但 .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(...)
returns 为 null 或实例,写入操作将使缓存无效。通过将 .get(...)
操作放入 synchronized(map)
块,我真的看不出哪里会出错或会改善什么。
这是一个简单的场景,它会在不同步的情况下产生问题 get
:
- 线程A启动
get
,计算哈希桶号,并被抢占
- 线程 B 调用
clear()
,因此分配了较小的存储桶数组
- 线程A苏醒,可能运行进入索引越界异常
这是一个更复杂的场景:
- 线程 A 锁定映射以进行更新,并被抢占
- 线程B发起
get
操作,计算哈希桶号,被抢占
- 线程 A 醒来,继续
put
,并意识到桶需要调整大小
- 线程 A 分配新桶,将旧内容复制到其中,并添加新项目
- 线程 B 醒来,并在新的桶数组上使用旧的桶索引继续搜索。
在这一点上,A 可能不会找到正确的项,因为它很可能位于不同索引处的哈希桶中。这就是 get
也需要同步的原因。
我在一个 HashMap 上使用来自多个线程的 .get(...)
、.put(...)
和 .clear()
操作。 .put(...)
和 .clear()
在 synchronized
块内,但 .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(...)
returns 为 null 或实例,写入操作将使缓存无效。通过将 .get(...)
操作放入 synchronized(map)
块,我真的看不出哪里会出错或会改善什么。
这是一个简单的场景,它会在不同步的情况下产生问题 get
:
- 线程A启动
get
,计算哈希桶号,并被抢占 - 线程 B 调用
clear()
,因此分配了较小的存储桶数组 - 线程A苏醒,可能运行进入索引越界异常
这是一个更复杂的场景:
- 线程 A 锁定映射以进行更新,并被抢占
- 线程B发起
get
操作,计算哈希桶号,被抢占 - 线程 A 醒来,继续
put
,并意识到桶需要调整大小 - 线程 A 分配新桶,将旧内容复制到其中,并添加新项目
- 线程 B 醒来,并在新的桶数组上使用旧的桶索引继续搜索。
在这一点上,A 可能不会找到正确的项,因为它很可能位于不同索引处的哈希桶中。这就是 get
也需要同步的原因。