zoukankan      html  css  js  c++  java
  • HashMap在高并发下引起的死循环

    HashMap事实上并非线程安全的,在高并发的情况下,是非常可能发生死循环的,由此造成CPU 100%,这是非常可怕的。所以在多线程的情况下,用HashMap是非常不妥当的行为,应採用线程安全类ConcurrentHashMap进行取代。


    HashMap死循环原因

    HashMap进行存储时,假设size超过当前最大容量*负载因子时候会发生resize。首先看一下resize原代码


    void resize(int newCapacity) {
            Entry[] oldTable = table;
            int oldCapacity = oldTable.length;
            if (oldCapacity == MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return;
            }
    
            Entry[] newTable = new Entry[newCapacity];
            transfer(newTable);
            table = newTable;
            threshold = (int)(newCapacity * loadFactor);
        }

    而这段代码中又调用了transfer()方法,而这种方法实现的机制就是将每一个链表转化到新链表,而且链表中的位置发生反转,而这在多线程情况下是非常easy造成链表回路。从而发生get()死循环。我们看一下他的源码

    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) {
                    src[j] = null;
                    do {
                        Entry<K,V> next = e.next;
                        int i = indexFor(e.hash, newCapacity);
                        e.next = newTable[i];
                        newTable[i] = e;
                        e = next;
                    } while (e != null);
                }
            }
        }

    HashMap死循环演示

    假如有两个线程P1、P2,以及链表 a=》b=》null

    1、P1先运行,运行完"Entry<K,V> next = e.next;"代码后发生堵塞,或者其它情况不再运行下去,此时e=a。next=b

    2、而P2已经运行完整段代码,于是当前的新链表newTable[i]为b=》a=》null

    3、P1又继续运行"Entry<K,V> next = e.next;"之后的代码,则运行完"e=next;"后,newTable[i]为a《=》b。则造成回路,while(e!=null)一直死循环


    总结

    HashMap并不是线程安全,所以在多线程情况下,应该首先考虑用ConcurrentHashMap。避免悲剧的发生




  • 相关阅读:
    C#对象初始化器
    C#构造方法
    C#方法重载
    C#方法
    C#类 对象 字段和属性
    C#数组
    C#字符串
    C#原码反码补码
    字段、方法、属性
    单例模式、异常
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/6957909.html
Copyright © 2011-2022 走看看