2011-12-19 59 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),那么这个代码是完全线程安全的。所以这只是一个非常有限的并发问题,可能这就是他们尚未解决的原因。