不正确使用HashMap,造成CPU 100%的问题

来源:互联网 发布:mac跟淘宝客服聊天 编辑:程序博客网 时间:2024/06/14 02:17
最近项目一启动服务器偶 缓存数据阶段就出现CPU占用100%的情况,以前不是每次都能reproduce. 昨天专心找了一下出现这种情况的case.
参考各类blog
不正确使用HashMap,造成CPU 100%的问题】和一些关于多线程的技术文档【浅谈Java多线程的同步问题】。
分析了一下.

先列出问题的代码:

public class DataManager {
   ......
    private Map<Object, Integer> fStyleCache = new HashMap<Object, Integer>();
    private Map<Object, Integer> fSkuCache = new HashMap<Object, Integer>();
   ......

   public static void initialize() {
        WorkerQueue q = new WorkerQueue();
       
        ..........
        q.add(new DBWorker() {
            @Override
            protected void process()
                throws Exception
            {
                DataManager dm = getInstance(this);
                dm.getBaseSscDataUnit(dm.getBasePOSStartEndMonth(), this);
            }
        });
        q.add(new DBWorker() {
            @Override
            protected void process()
                throws Exception
            {
                DataManager dm = getInstance(this);
                dm.getBaseSscDcDataUnit(dm.getBasePOSStartEndMonth(), this);
            }
        });
        /*q.add(new DBWorker() {
            @Override
            protected void process()
                throws Exception
            {
                DataManager dm = getInstance(this);
                dm.getBaseProdAttrDataUnit(this);
            }
        });
        */
        q.start();
    }

    ..........

    private StyleSkuCountDataUnit getBaseSscDataUnit(int[] posStartEndMonth, DBConnection dc)
            throws SQLException
    {
        synchronized(fSscDataUnit) {
            if(fSscDataUnit.IsNeedRetrieveDBAgain()) {
                fSscDataUnit = this.buildSscDataUnit(......);//此方法调用getStyleSkuFromCache
                fSscDataUnit.setIsNeedRetrieveDBAgain(false);
            }
        }
   
        return fSscDataUnit;
    }
   
    private StyleSkuCountDataUnit getBaseSscDcDataUnit(int[] posStartEndMonth, DBConnection dc)
            throws SQLException
    {
        synchronized(fSscDcDataUnit) {
            if(fSscDcDataUnit.IsNeedRetrieveDBAgain()) {
                fSscDcDataUnit = this.buildSscDcDataUnit(.......);//此方法调用getStyleSkuFromCache
                fSscDcDataUnit.setIsNeedRetrieveDBAgain(false);
            }
        }
   
        return fSscDcDataUnit;
    }

    private Integer getStyleSkuFromCache(Object styleSku, boolean isStyle){
        Map<Object, Integer> cache = null;
        if(isStyle){
            cache = fStyleCache; //HashMap
        }else{
            cache = fSkuCache; //HashMap
        }
        Integer value = cache.get(styleSku);
        if(null == value){
            value = cache.size();
            cache.put(styleSku, value);
        }

        return value;
    }   

}


在调用getStyleSkuFromCache的时候,高并发下,会出现hashmap的死锁,导致cpu100%。就来分析一下出现死锁的原因是什么。

每一次调用getStyleSkuFromCache的时候,会向cache的map中put value. 而从HashMap内部实现来看 源码如下:

void transfer(Entry[] newTable) {
        Entry[] src = table;
        intnewCapacity = newTable.length;
        for(intj = 0; j < src.length; j++) {
            Entry<K,V> e = src[j];
            if(e !=null) {
                src[j] =null;
                do{
                    Entry<K,V> next = e.next;
                    inti = indexFor(e.hash, newCapacity);
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                }while(e !=null);
            }
        }
}

从上面的代码看来,每一个线程进来都先执行.一开始fStyleCache ,fSkuCache是空的,两个线程在执行上面的do{} 操作同一个cache的map
,校验
while (e != null) 时出现同步问题e.next读的是对方线程刚put的value 这样e.next一直不为null,while一直被执行,变成了死循环。cpu就瞬间飙升到100%.总之,就是多线程操作(主要是修改map结构)同一个map导致内部map数据结构紊乱。

解决办法:

1.将HashMap改为ConcurrentHashMap,线程安全的Map。

2.对此方法 getStyleSkuFromCache(Object styleSku, boolean isStyle) 进行同步控制,单线程执行此方法。


原创粉丝点击