之前整理了一份Java中常用的集合类的基本特性:
Java常用集合类图解
详细介绍了HashMap:
HashMap之浅析
HashMap之Hash碰撞
HashMap之扩容机制
HashMap之线程安全
本文分析并补充一下线程安全的HashMap-ConcurrentHashMap
参考优秀博文: 深入并发包 ConcurrentHashMap
补充:
-
CocurrentHashMap能完全代替Hashtable吗?
我们知道Hashtable是synchronized的,会锁定整个map,当Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。ConcurrentHashMap同步性能更好,因为它仅仅根据同步级别对map的一部分进行上锁。
HashTable虽然性能上不如ConcurrentHashMap,但并不能完全被取代,两者的迭代器的一致性不同的,HashTable的迭代器是强一致性的,而ConcurrentHashMap是弱一致的。 ConcurrentHashMap的get,clear,iterator 都是弱一致性的。 Doug Lea 也将这个判断留给用户自己决定是否使用ConcurrentHashMap。
那么什么是强一致性和弱一致性呢?
get方法是弱一致的,是什么含义?可能你期望往ConcurrentHashMap底层数据结构中加入一个元素后,立马能对get可见,但ConcurrentHashMap并不能如你所愿。换句话说,put操作将一个元素加入到底层数据结构后,get可能在某段时间内还看不到这个元素,若不考虑内存模型,单从代码逻辑上来看,却是应该可以看得到的。 -
ConcurrentHashMap.computeIfAbsent 在 JDK 1.8 存在 BUG
public class ConcurrentHashMapDemo{
private Map<Integer,Integer> cache =new ConcurrentHashMap<>(15);
public static void main(String[]args){
ConcurrentHashMapDemo ch = new ConcurrentHashMapDemo();
System.out.println(ch.fibonaacci(80));
}
public int fibonaacci(Integer i){
if(i==0||i ==1) {
return i;
}
return cache.computeIfAbsent(i,(key) -> {
System.out.println("fibonaacci : "+key);
return fibonaacci(key -1)+fibonaacci(key - 2);
});
}
}
如果你将这个代码跑起来,你会发现的这个程序将进入死循环,而无法结束。通过阅读源码发现,ConcurrentHashMap.computeIfAbsent() 方法停留在了一个 ReservationNode 对象上,感兴趣的朋友可阅读java.util.concurrent.ConcurrentHashMap#transfer扩容源码
解决方法:
- 升级到JDK 1.9,已经修复该Bug
- 不要用递归的方式创建 ConcurrentHashMap 对象
技术讨论 & 疑问建议 & 个人博客
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议,转载请注明出处!
网友评论