《Java困惑》:多并发情况下HashMap是否还会产生死循环
今天本来想看下了ConcurrentHashMap的源码,ConcurrentHashMap是Java 5中支持高并发、高吞吐量的线程安全HashMap实现,
在看很多博客在介绍ConcurrentHashMap之前,都说HashMap适用于单线程访问,这是因为HashMap的所有方法都没有进行锁同步,因此是线程不安全的,不仅如此,当多线程访问的时候还容易产生死循环。
虽然自己在前几天的时候看过HashMap的源码,感觉思路啥啥的都还清楚,对于多线程访问只知道HashMap是线程不安全的,但是不知道HashMap在多线程并发的情况下会产生死循环呢,为什么会产生,何种情况下才会产生死循环呢???
正由于有这个问题,于是先将分析ConcurrentHashMap的源码的事情给放了一放,开始在网上查找这个问题的原因。
看了如下的博客,写的都挺好的,下面只列出最主要的一篇:
1、http://coolshell.cn/articles/9606.html/comment-page-1#comments (最源头的一篇文章,下面的截图均来自于这篇文章)
看的其它博客和资料或者是来自于上面这篇文章或者是说的意思差不多。
声明:
由于上面博客中所分析的HashMap的源码和我目前所看到的源码不一致,发现改动挺大的。目前我的JDK的版本是:jdk1.8.0_45.
这篇博文想讨论的问题是:在目前jdk1.8.0_45源码下,还存不存在上面列出的博文中所将到的这种死循环的问题,个人的答案是:不存在了。若有错,请批评指正
原HashMap的源码产生死循环的过程
下面贴出原HashMap的源码,这是原博客分析的基础,因此,截图如下:
第一篇博客中所贴出的HashMap的源码如下:
个人小结:根据原HashMap的源码,当我们想往HashMap中添加某个元素<K,V>
时,假如根据k的hash值找到的存储位置是在table中的index,且刚好此位置已经有了元素,即发生了碰撞,碰撞的处理方式为:将此元素加在此位置的链表的头部,注意,是加在链表头部。
在addEntry方法的代码中很容易的看出这一点。而addEntry方法是在put方法中检测该key在该位置不存在时调用的。
addEntry方法代码如下:
void addEntry(int hash, K key, V value, int bucketIndex)
{
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
//查看当前的size是否超过了我们设定的阈值threshold,如果超过,需要resize
if (size++ >= threshold)
resize(2 * table.length);
}
就因为是将结点加在链表的头部,所以就出现了原博文中介绍的死循环问题。
do {
Entry<K,V> next = e.next; // <--假设线程一执行到这里就被调度挂起了
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
死循环的产生具体过程描述如下(图解可以看原博文):
第一步:假设线程1刚记录下来e=3,next=7 就切换到线程2执行