有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

应使用的java ConcurrentHashMap putIfAbsent或锁定映射

我遇到一种情况,调用方希望根据当前实例(标识符)获取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上执行同步的第二个选项的问题,那个么应该使用哪一个呢


共 (2) 个答案

  1. # 1 楼答案

    先检查是否存在,然后再检查是否存在

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

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

  2. # 2 楼答案

    在您的情况下,没有选择:您无法锁定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

    如果您不想创建不必要的值,那么如果您使用的是JDK 8+,那么可以使用computeIfAbsent,您可以将实例化延迟到真正需要时