• HashMap之原理及死锁


    一、HashMap原理
    1.HashMap的本质就是数组和链表。table是一个entry数组,每一个数组元素保存一个Entry节点,而Entry节点内部又连接着同样key的下一个Entry节点,就构成了链表。.
    详情见 HashMap源码分析

    2.HashMap死锁原因:
    HashMap会造成死锁,因为HashMap是线程非安全的,多并发的情况容易造成死锁,若要高并发推荐使用ConcurrentHashMap。这里的加了锁。
    高并发时引起HashMap死锁的原因分析:
    HashMap死锁原因分析
    总的来说是:
    Hash表的初始大小(HashMap初始容量大小为16)有限,当put存入的数据增加时就必须扩容了,源码中会调用rehash方法(即是把原表内容移入到一个新的表中),单线程下rehash就是把原来链表遍历,从新的链表头部(为什么不加到新链表最后?因为复杂度是 O(N))挨个放入,放入的过程中依次执行函数transfer();

    Entry<K,V> next = e.next;//保留头指针的下一个节点——因为是单链表,如果要转移头指针,一定要保存下一个结点,不然转移后链表就丢了
    e.next = newTable[i];//插入到链表的头部——e 要插入到链表的头部,所以要先用 e.next 指向新的 Hash 表第一个元素(为什么不加到新链表最后?因为复杂度是 O(N))
    
    newTable[i] = e;//——现在新 Hash 表的头指针仍然指向 e 没转移前的第一个元素,所以需要将新 Hash 表的头指针指向 e
    
    e = next//——转移 e 的下一个结点
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    多线程的时候会出现同时
    put()操作,并进入了transfer()环节;

    假设现在有三个线程:T1,T2,T3;在老数组中的第一个数据也就是e引用的对象,我们称它为A,在新数组中的头两个数据分别为B和C,B.next=C。
    线程T1运行到e.next=newTable[i]时线程T2运行到next=e.next;然后线程t1去继续;
    运行,会产生什么效果呢?A.next=B,B.next=C。e指向的对象是B,newTable[i]=A。然后继续运行,e.next=newTable[i],也就是B.next=A;同时A.next=B,继续运行newTable[i] = e,e = next;如果没有其它线程捣乱的话,那么此时e应该是C啊,可惜只是如果,如果有第三个线程T3在线程T1执行e.next = newTable[i]的时候去执行next = e.next;那么就中途改变了next的值,本来是保存C的,但是现在成了A了。总结下现在的情况:e引用A,nextTable[i]引用B,A.next=B,B.next=A。现在明白了吧。会一直这么死锁下去的。

  • 相关阅读:
    window10 禁止更新
    安装node.msi 格式的文件失败
    url参数的转码和解码
    Linux12-内存管理
    C++四种cast
    Linux内核5-系统调用
    Linux内核3-进程管理
    UNIX12-线程(下)线程控制
    UNIX11-线程(上)
    Linux内核8-中断下半部和推后执行的工作(下半部)
  • 原文地址:https://www.cnblogs.com/tiancai/p/9056393.html
Copyright © 2020-2023  润新知