为什么同步变更操作的时候需要同步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 也需要同步的原因。