不正確使用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;
        intnewCapacity = 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;
                    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) 進行同步控制,單線程執行此方法。


發佈了32 篇原創文章 · 獲贊 9 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章