##
## HashMap的扩容原理
JDK7源码:
```java
为什么头插法会导致死锁?
想要搞明白插入死锁,得先了解扩容:
void resize(int newCapacity) {
Entry[] oldTable = table; //备份原数组
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) { //超过int最大值则不扩容
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity]; //创建新数组
transfer(newTable, initHashSeedAsNeeded(newCapacity)); //将老数组元素迁移到新数组
table = newTable; //更新table
}
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) { //遍历数组
while(null != e) { //遍历链表
Entry<K,V> next = e.next; //先记下next
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity); //计算新下标
e.next = newTable[i]; //愿链表成为e的next
newTable[i] = e; //e成为链表头
e = next;
}
}
}
死锁演示:
初始
线程A和线程B在put元素时同时触发了扩容,即将各自基于table进行扩容。
当线程B执行完 Entry<K,V> next = e.next; 时,线程A完成了扩容。
我们来分析下线程B接下来会发生什么:
for (Entry<K,V> e : table) { //遍历数组
while(null != e) { //遍历链表
Entry<K,V> next = e.next; //先记下next
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity); //计算新下标
e.next = newTable[i]; //愿链表成为e的next
newTable[i] = e; //e成为链表头
e = next;
}
}
按transfer()内部逻辑
线程B的第一次while循环:当前e是a,next是b,头插法:a→null
线程B的第二次while循环:e是b,next是a,头插法:b→a→null
线程B的第三次while循环:e是a,next是null,头插法:a→b→a→null,形成环,get时会陷入死循环。
线程B的第四次while循环:e是null,退出。
请介绍下
##
## 