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

时间:2015-08-30 18:56:12

标签: java multithreading synchronization

我在一个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(...)返回null或实例。我无法通过将.get(...)操作放入synchronized(map)块来确定可能出现的问题或改进措施。

1 个答案:

答案 0 :(得分:3)

这是一个简单的场景,会在非同步get上产生问题:

  • 线程A启动get,计算哈希桶号,并获取抢占
  • 线程B调用clear(),因此分配较小的存储区数组
  • 线程A醒来,可能会遇到索引越界异常

这是一个更复杂的场景:

  • 线程A锁定地图以进行更新,并获取预占
  • 线程B启动get操作,计算哈希桶号,然后抢占
  • 线程A醒来,继续put,并意识到存储桶需要调整大小
  • 线程A分配新存储桶,将旧内容复制到其中,然后添加新项目
  • 线程B唤醒,并使用新数据桶阵列上的旧存储桶索引继续搜索。

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