FindBugs 对 ConcurrentHashMap 的调用序列可能不是原子的

FindBugs sequence of calls to ConcurrentHashMap may not be atomic

我不明白为什么 FindBug 会抱怨这段代码

Foo obj = map.get(id);

if(obj == null) {
    obj = new Foo();
    map.put(id, obj);
}

obj.add(someObject);

第二个版本

Foo tmp = new Foo();
obj = map.putIfAbsent(id, tmp);
if (obj == null)
   obj = tmp

obj.add(someObject);

如果我做第二个版本,我每次都必须创建 Foo 对象。

Foo obj = map.get(id);

if(obj == null) {
    lock.writeLock().lock();
    try {
        obj = new Foo();
        map.put(id, obj);
    }finally {
        lock.writeLock().unlock();
    }
}

obj.add(someObject);

对于第三个版本,FindBugs 仍然抱怨不是原子的。

它说:

Sequence of calls to concurrenthashmap may not be atomic

将 put 更改为 putIfAbsent,FindBugs 仍然抱怨。

如果在您的线程正在评估 if () 语句时其他线程使用该键添加条目,您的线程将覆盖它。

您需要使用 putIfAbsent ,否则不能与地图互动。

Findbugs 观察到这对方法调用 map.get(id)map.put(id, obj) 可能不是原子的,这意味着其他线程可能会在两者之间修改 map

例如,另一个线程可能会记录这两个调用之间键 id 的不同映射,然后当第一个线程执行它的 map.put() 时,它会悄无声息地丢失。由于您费心测试该键最初是否存在映射,因此这个结果似乎不太可能是可以接受的。

解决此问题的一种方法是使用 ConcurrentHashMap.putIfAbsent() 代替 both 调用(而不仅仅是代替 put())。