[英]Is this incrementAndGet thread-safe? It seems to pull object from eh cache
這個servlet似乎是從ehCache中從具有此對象的Element中獲取一個對象: http : //code.google.com/p/adwhirl/source/browse/src/obj/HitObject.java? repo=servers-mobile
然后繼續增加一個原子長的計數器:
//Atomically record the hit
if(i_hitType == AdWhirlUtil.HITTYPE.IMPRESSION.ordinal()) {
ho.impressions.incrementAndGet();
}
else {
ho.clicks.incrementAndGet();
}
這對我來說似乎不是線程安全的,因為可能會從緩存中獲取多個線程,如果兩個線程同時增加,則可能會失去點擊/展示次數。
您是否同意這不是線程安全的?
AtomicLong
和AtomicInteger
在內部使用AtomicLong
比較和設置(或比較和交換)。 這個想法是您告訴CAS兩件事:您期望long / int擁有的值,以及您想要將其更新為的值。 如果long / int具有您說的應有的值,則CAS將自動進行更新並返回true
;否則,返回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將成功。
這種方法有一個賭注:如果爭用較低,則CAS的速度要比同步塊快,這不太可能導致線程上下文切換。 但是,如果存在很多爭用,則某些線程將不得不以每個增量進行多次循環迭代,這顯然會浪費工作。 一般來說,在大多數常見負載下,incrementAndGet將更快。
增量是線程安全的,因為AtomicInteger和Family保證了這一點。 但是從緩存中插入和提取存在一個問題,其中可以創建和插入兩個(或更多)HitObject。 這可能會導致在首次訪問此HitObject時丟失某些匹配。 正如@ denis.solonenko指出的那樣,代碼中已經有一個TODO可以解決此問題。
但是,我想指出的是,此代碼僅在首次訪問給定的HitObject時並發。 一旦將HitObject保存在緩存中(並且不再有創建或插入HitObject的線程),那么此代碼就完全是線程安全的。 因此,這只是一個非常有限的並發問題,這可能就是他們尚未解決此問題的原因。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.