应该使用哪个 - ConcurrentHashMap putIfAbsent 或锁定地图

Which should be used - ConcurrentHashMap putIfAbsent or locking the map

我有一种情况,调用者希望根据当前实例(标识符)获取 bean 的对象。现在,如果当前实例存在 bean 对象,则不应再次创建它。所以有两种方法(我认为)可以做到这一点 -

1. Using putIfAbsent of ConcurrentHashMap

beanObject = objectFactory.get(); // this will create new instance every time
// even if not required
beanObject = beanMapForInstance.putIfAbsent(name, beanObject);
return beanObject;

2. By Locking the map

beanObject = beanMapForInstance.get(name); // beanMapForInstance is a ConcurrentHashMap
if(beanObject == null){
    synchronized (beanMapForInstance) {
        beanObject = beanMapForInstance.get(name);
        if(beanObject == null){
            beanObject = objectFactory.getObject();
            beanMapForInstance.put(name, beanObject);
        }
    }
}
return beanObject;

在第一种方法中,每次都会创建新对象,所以我认为应该首选第二种方法。但是 findbugs 显示了在 ConcurrentHashMap 上执行同步的第二个选项的问题,那么应该使用哪个?

先检查是否存在,然后再检查 putIfAbsent 怎么样?

beanObject = beanMapForInstance.get(name);
if (beanObject == null){
    beanObject = beanMapForInstance.putIfAbsent(
      name, objectFactory.get());
} 

将在 99.99% 的时间工作,并且在非常罕见的竞争条件情况下,您调用冗余构造函数(除了性能方面的考虑,这对您来说显然不是问题)。

在你的情况下,别无选择:你无法锁定 ConcurrentHashMap。更准确地说,锁定 ConcurrentHashMap 并没有像您预期的那样工作。它不会使您的操作线程安全,因为 ConcurrentHashMap 中的线程安全操作不是通过同步对象本身来实现的。

引自Javadoc of ConcurrentHashMap:

This class is fully interoperable with Hashtable in programs that rely on its thread safety but not on its synchronization details

如果你不想不必要地创建一个值,那么你可以使用 computeIfAbsent 如果你正在使用 JDK 8+,你可以延迟实例化直到真正需要它。