參考各類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的時候,會向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) 進行同步控制,單線程執行此方法。