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

时间:2015-04-16 06:53:23

标签: java multithreading spring concurrency synchronization

我有一种情况,调用者希望根据当前实例(标识符)获取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 个答案:

答案 0 :(得分:1)

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

引自Javadoc of ConcurrentHashMap

  

该类与依赖其线程安全但不依赖于其同步细节的程序中的Hashtable完全可互操作

如果您不想不必要地创建一个值,那么如果您使用的是JDK 8+,则可以使用computeIfAbsent,这可以延迟实例化,直到真正需要它为止。

答案 1 :(得分:0)

如何首先检查存在,然后才将putIfAbsent?

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

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