美文网首页
专题一 HashMap线程安全问题(三)

专题一 HashMap线程安全问题(三)

作者: RainySpring | 来源:发表于2020-03-23 00:37 被阅读0次

线程安全性

在多线程使用场景中,应该尽量避免使用线程不安全的HashMap,而使用线程安全的ConcurrentHashMap。那么为什么说HashMap是线程不安全的,下面举例子说明在并发的多线程使用场景中使用HashMap可能造成死循环。代码例子如下(便于理解,仍然使用JDK1.7的环境):

public class HashMapInfiniteLoop {  

    private static HashMap<Integer,String> map = new HashMap<Integer,String>(2,0.75f);  
    public static void main(String[] args) {  
        map.put(5, "C");  

        new Thread("Thread1") {  
            public void run() {  
                map.put(7, "B");  
                System.out.println(map);  
            };  
        }.start();  
        new Thread("Thread2") {  
            public void run() {  
                map.put(3, "A);  
                System.out.println(map);  
            };  
        }.start();        
    }  
}

其中,map初始化为一个长度为2的数组,loadFactor=0.75,threshold=2*0.75=1,也就是说当put第二个key的时候,map就需要进行resize。

通过设置断点让线程1和线程2同时debug到transfer方法的首行。注意此时两个线程已经成功添加数据。放开thread1的断点至transfer方法的“Entry next = e.next;” 这一行;然后放开线程2的的断点,让线程2进行resize。结果如下图。


image.png

注意,Thread1的 e 指向了key(3),而next指向了key(7),其在线程二rehash后,指向了线程二重组后的链表。

线程一被调度回来执行,先是执行 newTalbe[i] = e, 然后是e = next,导致了e指向了key(7),而下一次循环的next = e.next导致了next指向了key(3)。

image.png

e.next = newTable[i] 导致 key(3).next 指向了 key(7)。注意:此时的key(7).next 已经指向了key(3), 环形链表就这样出现了。


image.png

于是,当我们用线程一调用map.get(11)时,悲剧就出现了——Infinite Loop。

再回头看下之前源码部分在transfer方法:

void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        //1、table
        for (Entry<K,V> e : table) {
            while(null != e) {
                //2、next
                Entry<K,V> next = e.next;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
    }

多线程环境下, 其他线程已经扩容成功,此时的table引用已经指向新的table,而正在扩容中的线程对于next节点的引用还是老table节点的引用顺序(顺序),而新table节点顺序恰好是相反的(倒置),导致next寻找节点方向反过来了,形成了一个闭环。

jdk7闭环问题总结

表面上是因为头插入导致在扩容时transfer进行数据迁移过程中出现节点倒置,

相关文章

网友评论

      本文标题:专题一 HashMap线程安全问题(三)

      本文链接:https://www.haomeiwen.com/subject/leabyhtx.html