我在一个HashMap上从多个线程使用.get(…)、. put(…)和.clear()操作. .put(…)和.clear()在同步块内,但.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(...));
}
}
}
很明显只是:
synchronized (map) {
map.clear();
}
由于已同步,写操作将使缓存无效,并且get(…)返回null或实例.我真的看不出通过将.get(…)操作放入synced(map)块中会出什么问题或会改善什么.
解决方法:
这是一个简单的方案,它将在不同步的get上产生问题:
>线程A开始获取,计算哈希桶数,并被抢占
>线程B调用clear(),因此分配了较小的存储桶数组
>线程A唤醒,并且可能会遇到index-out-of-bounds异常
这是一个更复杂的方案:
>线程A锁定地图以进行更新,并被抢占
>线程B启动get操作,计算哈希桶数,并被抢占
>线程A唤醒,并继续放置,并意识到需要调整大小的存储桶
>线程A分配新的存储桶,将旧内容复制到其中,并添加新的存储桶
>线程B唤醒,并在新的存储桶阵列上使用旧的存储桶索引继续搜索.
此时,A可能不会找到正确的项目,因为它很可能位于不同索引的哈希存储桶中.这就是为什么get也需要同步的原因.