Race Condition引起的性能問題

Race Condition(也叫做資源競爭),是多線程編程中比較頭疼的問題。特別是Java多線程模型當中,經常會因爲多個線程同時訪問相同的共享數據,而造成數據的不一致性。爲了解決這個問題,通常來說需要加上同步標誌“synchronized”,來保證數據的串行訪問。但是“synchronized”是個性能殺手,過多的使用會導致性能下降,特別是擴展性下降,使得你的系統不能使用多個CPU資源。  這是我們在性能測試中經常遇見的問題。

可是上個星期我卻遇見了相反的情況:因爲缺少同步標誌也同樣會使性能受影響。

那是一個ERP系統,運行在我們的T2000服務器(8核32線程)上。當500個併發用戶的時候居然把所有的CPU都壓得滿滿的(90%以上的忙碌)。這是很少有的現象,在我測試的所有項目中很少有擴展性這麼好的系統能把T2000的32個線程都佔滿的。我狠狠的誇了他們的應用。話音沒落,卻發現測試結果很差,平均響應時間很長。不可能呀,所有的CPU都在幹活,而且都在用戶態(如果在系統態幹太多的活就有問題了),結果怎麼還會差呢。CPU都在幹嘛呢?

通過工具發現(Dtrace for Java),我們發現很多的CPU都在做一件事情,那就是不停的執行一條Java語句(HashMap.get())。象是進入了死循環。我們進行了進一步試驗,讓併發用戶數量爲1,不停的運行10分鐘,結果沒有發現這種情況;接着我們讓50個併發用戶同時運行,但是隻運行在一個CPU上(通過psrset),結果也沒有出現死循環狀態。只要併發用戶數量超過10個,運行的CPU超過兩個,不到2分鐘就出現死循環。一旦死循環出現,大量CPU資源被白白浪費,性能自然很差。

通過上面的試驗我們可以很肯定的判斷,是由於併發控制不好,導致數據的不一致,引起的死循環。值得一提的是,HashMap不是一個線程安全的數據結構,要用到多個線程中去,需要自己加上同步標誌,爲什麼會死循環呢,看看下面HashMap中get函數的源代碼:

public V get(Object key) {
if (key == null)
    return getForNullKey();
       int hash = hash(key.hashCode());
       for (Entry<K,V> e = table[indexFor(hash, table.length)];
            e != null;
            e = e.next) {
           Object k;
           if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
               return e.value;
       }
       return null;
   }

get函數會根據key的hashcode來鎖定多個對象,並且遍歷這些對象來找到key所對應的對象。當多個線程不安全的修改HanshMap數據結構的時候,有可能使得這個函數進入死循環。

我們建議客戶使用ConcurrentHashMap或在使用HanshMap的時候加上同步標誌,問題得到解決!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章