HashMap的死锁 与 ConcurrentHashMap
HashMap
Java7,HashMap 会产生死锁; 数据模型:数组 + 链表
Java8,HashMap 不会产生死锁,同时put有可能会产生数据丢失的情况;数据模型:数组 + 链表 + 红黑树
JAVA7 HashMap死锁的原因:hashMap在多线程的场景下,扩容期间存在节点位置互换指针引用,有可能导致死环;
扩容的阈值:threshold = 16 * 0.75,超过阈值就会去扩容。
java7 的 HashMap 的 resize() 方法:
void resize(int newCapacity) { //传入新的容量
Entry[] oldTable = table; //引用扩容前的Entry数组
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) { //扩容前的数组大小如果已经达到最大(2^30)了
threshold = Integer.MAX_VALUE; //修改阈值为int的最大值(2^31-1),这样以后就不会扩容了
return;
}
Entry[] newTable = new Entry[newCapacity]; //初始化一个新的Entry数组
transfer(newTable); //!!将数据转移到新的Entry数组里
table = newTable; //HashMap的table属性引用新的Entry数组
threshold = (int)(newCapacity * loadFactor);//修改阈值
}
transfer方法:
void transfer(Entry[] newTable) {
Entry[] src = table; //src引用了旧的Entry数组
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) { //遍历旧的Entry数组
Entry<K,V> e = src[j]; //取得旧Entry数组的每个元素
if (e != null) {
src[j] = null;//释放旧Entry数组的对象引用(for循环后,旧的Entry数组不再引用任何对象)
do {
Entry<K,V> next = e.next;
int i = indexFor(e.hash, newCapacity); //!!重新计算每个元素在数组中的位置
e.next = newTable[i]; //标记[1]
newTable[i] = e; //将元素放在数组上
e = next; //访问下一个Entry链上的元素
} while (e != null);
}
}
}
ConcurrentHashMap
Java7, Segment 继承 ReentrantLock
Java8,synchronized加锁,使用 CAS
Java1.7 HashMap死锁的原因:hashMap在多线程的场景下,扩容期间存在节点位置互换指针引用,有可能导致死环;
扩容的阈值:threshold = 16 * 0.75,超过阈值就会去扩容。