2011-12-19 57 views
2

這個servlet似乎來從ehcache的一個對象,由具有此對象的元素:http://code.google.com/p/adwhirl/source/browse/src/obj/HitObject.java?repo=servers-mobile這是incrementAndGet線程安全的嗎?這似乎從誒緩存拉對象

它然後繼續遞增計數器,它是一個原子長:

http://code.google.com/p/adwhirl/source/browse/src/servlet/MetricsServlet.java?repo=servers-mobile#174

//Atomically record the hit 
    if(i_hitType == AdWhirlUtil.HITTYPE.IMPRESSION.ordinal()) { 
     ho.impressions.incrementAndGet(); 
    } 
    else { 
     ho.clicks.incrementAndGet(); 
    } 

這似乎是線程安全不把我當多個線程可以從緩存如果兩個增量你可能會失去一個點擊/展示次數的同時獲取。

你是否同意這不是線程安全的?

+2

增量部分非常安全。獲取和存儲'ho'對象的方式可能有一些問題,這是在代碼本身中用「TODO」 – 2011-12-19 22:37:59

回答

6

AtomicLong and AtomicInteger在內部使用CAS - 比較和設置(或比較和交換)。這個想法是,你告訴CAS兩件事:你期望long/int的值,以及你想要更新它的值。如果long/int具有您所說的值,CAS將自動進行更新並返回true;否則,它不會進行更新,它將返回false。許多現代芯片在機器碼級非常有效地支持CAS;如果JVM運行在沒有CAS的環境中,它可以使用互斥體(Java調用同步)來實現CAS。無論如何,一旦你有一個CAS,您可以安全地實現通過這個邏輯原子增量(以僞代碼):

long incrementAndGet(atomicLong, byIncrement) 
    do 
     oldValue = atomicLong.get()   // 1 
     newValue = oldValue + byIncrement 
    while ! atomicLong.cas(oldValue, newValue) // 2 
    return newValue 

如果另一個線程已經到來,並且做線// 1// 2之間自身的增量,中科院將失敗,循環將再次嘗試。否則,CAS將成功。

在這種方法中有一種賭博:如果競爭較少,CAS比同步塊更快不會導致線程上下文切換。但是如果存在很多爭用,一些線程將不得不經歷每個增量的多次循環迭代,這顯然相當於浪費工作。一般來說,在最常見的負載下,incrementAndGet會更快。

0

自AtomicInteger和family保證增量是線程安全的。但是從緩存中插入和提取時存在問題,其中可以創建並插入兩個(或更多)HitObject。這會在第一次訪問HitObject時導致潛在的丟失命中。正如@ denis.solonenko指出的那樣,代碼中已經有一個TODO來解決這個問題。

但是我想指出的是,這個代碼只在第一次訪問給定HitObject時遇到併發性。一旦你在緩存中有HitObject(並且沒有更多的線程創建或插入HitObject),那麼這個代碼是完全線程安全的。所以這只是一個非常有限的併發問題,可能這就是他們尚未解決的原因。