高并发下的HashMap
1.Hashmap在插入元素过多的时候需要进行Resize,Resize的条件是
HashMap.Size >= Capacity * LoadFactor。
2.Hashmap的Resize包含扩容和ReHash两个步骤,ReHash在并发的情况下可能会形成链表环
HashMap进行存储时,假设size超过当前最大容量*负载因子时候会发生resize。首先看一下resize原代码
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()方法,而这种方法实现的机制就是将每一个链表转化到新链表,而且链表中的位置发生反转,而这在多线程情况下是非常easy造成链表回路。从而发生get()死循环。我们看一下他的源码
void transfer(Entry[] newTable) {
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {
Entry<K,V> e = src[j];
if (e != null) {
src[j] = null;
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);
}
}
}
HashMap死循环演示
假如有两个线程P1、P2,以及链表 a=》b=》null
1、P1先运行,运行完"Entry<K,V> next = e.next;"代码后发生堵塞,或者其它情况不再运行下去,此时e=a。next=b
2、而P2已经运行完整段代码,于是当前的新链表newTable[i]为b=》a=》null
3、P1又继续运行"Entry<K,V> next = e.next;"之后的代码,则运行完"e=next;"后,newTable[i]为a《=》b。则造成回路,while(e!=null)一直死循环