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

最近项目一启动服务器偶 缓存数据阶段就出现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;
         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);
             }
         }
}

从上面的代码看来,每一个线程进来都先执行. 一开始 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) 进行同步控制,单线程执行此方法。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值