【图解】HashMap1.7 头插法造成死循环的原因

1.概述

HashMap1.7当中,扩容的时候,采用的是头插法转移结点,在多线程并发的情况下会造成链表死循环的问题。

2.图解

假设有两个线程,线程1和线程2,两个线程进行hashMap的put操作,触发了扩容。

下面是扩容的时候结点转移的关键代码

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) {
    
    //两个线程都先进入if
              src[j] = null; 
              do {
    
     
                  Entry<K,V> next = e.next; 
                 int i = indexFor(e.hash, newCapacity);
                 e.next = newTable[i]; //线程1 这里还没执行 停下
                 newTable[i] = e;  
                 e = next;             
             } while (e != null);
         }
     }
 }

线程1和线程2 都进入if,然后线程1没有拿到cpu的资源在上面代码注释的地方停下了。此时的变量指针如下图所示:

在这里插入图片描述

记住 线程1中 E变量指向a结点,next变量指向b结点。

下面是线程2 拿到cpu的资源,执行结点转移

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

线程2停下,轮到线程1

因为之前线程1中E变量指向的是a结点,next变量指向的是b结点,所以如下图所示:

在这里插入图片描述

再来看看 刚才线程是在e.next = newTable[i] 这句代码还没执行的时候停下的,那么现在就要执行这一句代码

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) {
    
    //两个线程都先进入if
              src[j] = null; 
              do {
    
     
                  Entry<K,V> next = e.next; 
                 int i = indexFor(e.hash, newCapacity);
                 e.next = newTable[i]; //线程1刚才在这里停下,所以现在从这一句代码开始执行
                 newTable[i] = e;  
                 e = next;             
             } while (e != null);
         }
     }
 }

此时线程1 执行代码之后,就造成了链表的死循环,结果如下:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/gongsenlin341/article/details/112582217