我是java世界的新bie并探索并发哈希映射,在探索并发hashmap API时,我发现了putifAbsent()方法
public V putIfAbsent(K paramK, V paramV)
{
if (paramV == null)
throw new NullPointerException();
int i = hash(paramK.hashCode());
return segmentFor(i).put(paramK, i, paramV, true);
}
现在请告知它的功能是什么,我们什么时候需要它,如果可能的话请用一个简单的例子来解释。
答案 0 :(得分:12)
ConcurrentHashMap
的设计使其可以被大量并发Thread
使用。
现在,如果你使用标准Map
接口提供的方法,你可能会写这样的东西
if(!map.containsKey("something")) {
map.put("something", "a value");
}
这看起来不错,似乎可以胜任,但它不是线程安全的。所以你会想,“啊,但我知道synchronized
关键字”并将其改为此
synchronized(map) {
if(!map.containsKey("something")) {
map.put("something", "a value");
}
}
解决了这个问题。
现在你所做的是锁定整个地图以便读取和写入,同时检查密钥是否存在,然后将其添加到地图中。
这是一个非常粗糙的解决方案。现在,您可以使用双重检查锁实现自己的解决方案并重新锁定密钥等,但很多 非常复杂的代码非常容易出错。
因此,您使用JDK提供的解决方案。
ConcurrentHashMap
是一个聪明的实现,它将Map
分成区域并单独锁定它们,这样您就可以在没有外部锁定的情况下对映射进行并发,线程安全,读写。
与实现中的所有其他方法一样,putIfAbsent
锁定密钥的区域而不是整个Map
,因此允许其他区域同时继续进行其他操作。
答案 1 :(得分:0)
当多个线程可以同时访问同一个映射时,使用ConcurrentHashMap。在这种情况下,手动实现putIfAbsent(),如下所示:
if (!map.containsKey(key)) {
map.put(key, value);
}
实际上,两个线程可以并行执行上面的块并进入竞争条件,其中两个都首先测试密钥是否不存在,然后两者都将它们自己的值放在映射中,从而打破了程序的不变量。
ConcurrentHashMap因此提供putIfAbsent()
操作,确保以原子方式完成,避免竞争条件。
答案 2 :(得分:0)
想象一下,我们需要一个懒惰初始化的命名单例bean的缓存。下面是基于ConcurrentHashMap的无锁实现:
ConcurrentMap<String, Object> map = new ConcurrentHashMap<>();
<T> T getBean(String name, Class<T> cls) throws Exception {
T b1 = (T) map.get(name);
if (b1 != null) {
return b1;
}
b1 = cls.newInstance();
T b2 = (T) map.putIfAbsent(name, b1);
if (b2 != null) {
return b2;
}
return b1;
}
请注意,它解决了与双重检查锁定相同的问题,但没有锁定。