风止雨歇

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,超过阈值就会去扩容。

posted on 2020-06-30 00:02  风止雨歇  阅读(612)  评论(0编辑  收藏  举报

导航