为什么在同步change操作时需要同步HashMap.get(key)?

Chr*_*ian 4 java multithreading synchronization

我在一个 HashMap 上使用来自多个线程的.get(...),.put(...).clear()操作。.put(...)并且.clear()在一个synchronized块内但.get(...)不是。我无法想象这会导致问题,但在我见过的其他代码.get()中几乎总是同步的。

获取/放置的相关代码

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(...));
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

明确只是:

synchronized (map) {
  map.clear();
}
Run Code Online (Sandbox Code Playgroud)

由于同步,写入操作将使缓存无效,并且get(...)返回 null 或实例。我真的看不出什么地方会出错,或者通过将.get(...)操作放入一个synchronized(map)块会改进什么。

das*_*ght 5

这是一个简单的场景,它会在 unsynchronized 上产生问题get

  • 线程 A 启动get,计算哈希桶号,并被抢占
  • 线程 B 调用clear(),因此分配了较小的存储桶数组
  • 线程A唤醒,可能会遇到index-out-of-bounds异常

这是一个更复杂的场景:

  • 线程 A 锁定地图以进行更新,并被抢占
  • 线程 B 发起get操作,计算哈希桶号,并被抢占
  • 线程A醒来,继续执行put,意识到桶需要调整大小
  • 线程 A 分配新桶,将旧内容复制到其中,并添加新项目
  • 线程 B 醒来,并使用新的桶数组上的旧桶索引继续搜索。

此时,A 可能不会找到正确的项目,因为它很可能位于不同索引的哈希桶中。这就是为什么也get需要同步的原因。