• 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的源码阅读理解中,也需要结合这个模型来考虑多线程环境下为什么不会出现这些冲突,类似的操作中它们是如何规避这种问题的。

  • 相关阅读:
    201521123033《Java程序设计》第10周学习总结
    201521123033《Java程序设计》第9周学习总结
    201521123033《Java程序设计》第8周学习总结
    201521123033《Java程序设计》第7周学习总结
    201521123033《Java程序设计》第6周学习总结
    201521123033《Java程序设计》第5周学习总结
    一个Markdown的例子
    201521123033《Java程序设计》第4周学习总结
    201521123033《Java程序设计》第3周学习总结
    maven私服
  • 原文地址:https://www.cnblogs.com/bruceChan0018/p/8033897.html
Copyright © 2020-2023  润新知