HashMap在并发环境下的死循环分析

      今天在看《分布式java应用》这本书的时候看到作者提到HashMap在多线程并发的环境下有可能出现死循环,导致cpu100%的现象,看了下源码结合网上的分析说明下这种可能性。可能出现问题的地方是在扩容的时候

void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } Entry[] newTable = new Entry[newCapacity]; transfer(newTable); table = newTable; threshold = (int)(newCapacity * loadFactor); }

 

这个方法本身没有问题,问题出在transfer(newTable);这个方法是用来移动oldTable里的数据到newTable里。

/** * Transfers all entries from current table to newTable. */ void transfer(Entry[] newTable) { Entry[] src = table; int newCapacity = newTable.length; for (int j = 0; j < src.length; j++) { //(1) Entry<K,V> e = src[j]; if (e != null) { src[j] = null; do { //(2) Entry<K,V> next = e.next; int i = indexFor(e.hash, newCapacity); //(3) e.next = newTable[i]; newTable[i] = e; e = next; } while (e != null); } } }

下面分析可能出现的情况,假设原来oldTable里存放a1,a2的hash值是一样的,那么entry链表顺序是:

P1:oldTable[i]->a1->a2->null

P2:oldTable[i]->a1->a2->null

线程P1运行到(1)下面这行时,e=a1(a1.next=a2),继续运行到(2)下面时,next=a2。这个时候切换到线程P2,线程P2执行完这个链表的循环。如果恰a1,a2在新的table中的hash值又是一样的,那么此时的链表顺序是: 

主存:newTable[i]->a2->a1->null

注意这个时候,a1,a2连接顺序已经反了。现在cpu重新切回P1,在(3)这行以后:e.next = newTable[i];即:a1.next=newTable[i];

newTable[i]=a1;

e=a2;

开始第二次while循环(e=a2,next=a1):

a2.next=newTable[i];//也就是a2.next=a1

newTable[i]=a2

e=a1

开始第三次while循环(e=a1,next=null)

a1.next=newTable[i];//也就是a1.next=a2


这个时候a1.next=a2,a2.next=a1,形成回环了,这样就造成了死循环,在get操作的时候next永远不为null,造成死循环。

可以看到很偶然的情况下会出现死循环,不过一旦出现后果是非常严重的,多线程的环境还是应该用ConcurrentHashMap。

 

 

 

你可能感兴趣的:(java,多线程,HashMap,null,table)