HashMap多线程下不安全的具体体现

  比较容易想到的是多线程环境下,如果几个线程同时在一个位置table[i]进行添加或者删除操作,会出现被覆盖或者其它情况。但还有一种比较严重的问题,即在多线程同时操作一个HashMap,进行扩容重排的过程中,有可能会出现环形链表,在下一次进行get操作或者迭代操作时,这里简单地结合JVM解释一下为什么在多线程环境下会出现环形链表。

  首先要清楚HashMap扩容时的几个步骤,这里以一个链table[i]为例:

  • 循环取出table[i]中的元素e1->e2->null
  • 根据indexFor计算这些元素的新的位置,假设这些元素经过indexFor函数计算后聚集在新的newTable[k]这个点上
  • 由于插入的顺序是先进入的元素会被后续的元素挤到next的位置,所以新的顺序为e2->e1->null

  单线程中这样做没什么问题,但是多线程环境下,每个子线程会存在本地内存(栈)与共享内存(堆)的读写刷新,如果多个线程同时对该表进行扩容,就有可能出现环形链表,下面还是举上例的多线程模式,模拟出出现环链的情况:

  • 首先T1和T2两个线程都判断出table需要扩容为newTable,还是假设元素顺序为e1->e2->null,且新表中的indexFor结果为newTable[k]
  • T1此时进行循环遍历,关键代码如下:
    /**
     * Transfers all entries from current table to newTable.
     */
    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;         //----------------->位置1
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                int k = indexFor(e.hash, newCapacity);
                e.next = newTable[k];
                newTable[k] = e;
                e = next;
            }
        }
    }
  • 当从table中取出e1,遍历到位置1时,线程T1被挂起,此时线程T1内的Entry<K,V> next = e.next已经被保存,存储的next为本地变量,保存在本地工作内存中,为(e2,指向null)
  • 线程T1被挂起后,线程T2完成整个table的扩容过程,此时在共享内存中,newTable已经出现,并且在新的表中,链表顺序变为e2->e1->null,此时newTable[k]为(e2,指向e1)
  • 线程T1此时继续执行,e.next此时为newTable[k](e2,指向e1),而存入newTable[k]的元素变为此时本地内存中的变量(e1,指向e2),此时在newTable[k]这个位置,环形链表就已经形成。

  在分析HashMap环链形成这个问题中,可以发现,多线程环境下要分析这种冲突问题,需要了解哪些变量是存在栈,哪些是存在堆中,他们之间如何协同,有哪些可能会形成冲突等,所以JVM模型对于此类问题的理解至关重要,在后续的HashTable,ConcurrentHashMap的源码阅读理解中,也需要结合这个模型来考虑多线程环境下为什么不会出现这些冲突,类似的操作中它们是如何规避这种问题的。

转载于:https://www.cnblogs.com/bruceChan0018/p/8033897.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值