这个servlet似乎从具有该对象的Element中获取ehCache中的对象: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();
}
这对我来说似乎不是线程安全的,因为多个线程可以从缓存中获取,如果两个线程同时增加,则可能会丢失点击/印象数。
您是否同意这不是线程安全的?
答案 0 :(得分:6)
AtomicLong
和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将成功。
这种方法存在赌博:如果争用率较低,则CAS比同步块更快,不会导致线程上下文切换。但是如果存在很多争用,一些线程将不得不按每个增量进行多次循环迭代,这显然等于浪费了工作。一般来说,在大多数常见负载下,incrementAndGet会更快。
答案 1 :(得分:0)
增量是线程安全的,因为AtomicInteger和family保证。但是从缓存中插入和获取存在问题,其中可以创建和插入两个(或更多)HitObject。这将导致在第一次访问此HitObject时可能会丢失一些命中。正如@ denis.solonenko指出的那样,代码中已经有一个TODO可以解决这个问题。
但是我想指出这个代码在首次访问给定的HitObject时只会遇到并发问题。一旦你在缓存中有HitObject(并且没有更多线程创建或插入HitObject),那么这段代码是完全线程安全的。所以这只是一个非常有限的并发问题,也许这就是他们尚未修复它的原因。