复合操作中的Java原子类

时间:2011-03-17 09:56:57

标签: java

如果多个线程调用“incrementCount”方法,以下代码是否会导致竞争条件问题?

public class sample {

       private AtomicInteger counter = new AtomicInteger(0);

       public int getCurrentCount {
          int current = counter.getAndIncrement();
          if (counter.compareAndSet(8, 0)) current = 0;
          return current;
      }
}

如果它导致竞争条件,除了使用synchronized关键字之外,还有什么可能的解决方案?

5 个答案:

答案 0 :(得分:3)

你可能不想让计数器超过8,这是行不通的。有竞争条件。

看起来你想要一个mod 8计数器。最简单的方法是单独使用AtomicInteger并使用类似

的内容
int current = counter.getAndIncrement() & 7;

(这是% 8的固定和优化版本)。对于mod 8的计算或任何其他2的幂,它可以完美地工作,对于其他数字,你需要% N并且遇到int溢出到负数的问题。

直接解决方案如下

public int getCurrentCount {
    while (true) {
        int current = counter.get();
        int next = (current+1) % 8;
        if (counter.compareAndSet(current, next))) return next;
    }
}

这是关于getAndIncrement()本身的工作原理,只是稍作修改。

答案 1 :(得分:2)

是的,它可能没有你想要的(有一种竞争条件)。

  1. 一个帖子可以拨打getAndIncrement()并收到8
  2. 第二个主题可能会调用getAndIncrement()并收到9
  3. 第一个帖子尝试compareAndSet,但值不是8
  4. 第二个帖子尝试compareAndSet,但值不是8
  5. 如果没有溢出的风险,你可以做类似

    的事情
    return counter.getAndIncrement() % 8;
    

    依靠那些不会溢出的东西对我来说似乎是一个糟糕的主意,我可能会粗略地做你做的事情,但让方法为synchronized

答案 2 :(得分:1)

基于getAndIncrement()的代码

public int getCurrentCount() {
  for(;;) {
    int courrent = counter.get();
    int next = current + 1;
    if (next >= 8) next = 0;
    if (counter.compareAndSet(current, next))
      return current;
  }
}

然而,在您的情况下,更简单的实现是

public int getCurrentCount() {
  return counter.getAndIncrement() & 0x7;
}

答案 3 :(得分:1)

你想要达到什么目的?即使您使用ajoobe或maartinus提出的修复程序,您也可以得到不同的线程获得相同的答案 - 考虑同时运行20个线程。我在这里看到这个“计数器”时没有看到任何有趣的意义 - 你也可以选择0到8之间的随机数。

答案 4 :(得分:0)

我认为你想要的是一个0到7的计数器形式。

如果是这种情况,则可能发生竞争条件,并且计数器的值可以变为9。

除非您可以使用%soln。正如其他人所说,你必须使用synchronized。