8

我正在浏览java.util.concurrent.atomic.AtomicInteger的源代码,以了解该类提供的原子操作是如何实现原子性的。比如AtomicInteger.getAndIncrement()方法源码如下

public final int getAndIncrement() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return current;
    }
}

我无法理解在无限 for 循环中编写操作序列的目的。它在 Java 内存模型 (JMM) 中是否有任何特殊用途。请帮我找到一个描述性的理解。提前致谢。

4

3 回答 3

7

我无法理解在无限 for 循环中编写操作序列的目的。

这段代码的目的是确保volatile字段得到适当的更新,而没有synchronized锁的开销。除非有大量线程都在竞争更新同一个字段,否则这很可能会旋转几次才能完成。

volatile关键字提供可见性和内存同步保证,但本身并不能确保具有多个操作(测试和设置)的原子操作。如果您正在测试然后设置一个volatile字段,那么如果多个线程试图同时执行相同的操作,则会出现竞争条件。在这种情况下,如果多个线程试图同时增加AtomicInteger,您可能会错过其中一个增量。此处的并发代码使用自旋循环和compareAndSet底层方法来确保volatile int仅在仍等于 3 时才更新为 4(例如)。

  1. t1 得到 atomic-int 并且它是 0。
  2. t2 得到 atomic-int 并且它是 0。
  3. t1 加 1
  4. t1原子地测试以确保它是 0,它是,并存储 1。
  5. t2 加 1
  6. t2原子地测试以确保它是 0,它不是,所以它必须旋转并重试。
  7. t2 得到 atomic-int 并且它是 1。
  8. t2 加 1
  9. t2原子地测试以确保它是 1,它,并存储 2。

它在 Java 内存模型 (JMM) 中是否有任何特殊用途。

不,它服务于类和方法定义的目的,并使用JMM 和周围的语言定义volatile来实现其目的。JMM 定义了语言对synchronizedvolatile和其他关键字的作用以及多个线程如何与缓存和中央内存交互。这主要是关于本机代码与操作系统和硬件的交互,很少(如果有的话)关于 Java 代码。

它是通过调用主要是带有一些包装器的本机方法compareAndSet(...)的类来更接近 JMM 的方法:Unsafe

public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
于 2013-06-05T13:02:38.767 回答
6

我无法理解在无限 for 循环中编写操作序列的目的。

为了理解为什么它处于无限循环中,我发现了解它的compareAndSet作用以及它如何返回 false 很有帮助。

Atomically sets the value to the given updated value if the current
value == the expected value.

 Parameters:
     expect - the expected value
     update - the new value 
 Returns:
     true if successful. False return indicates that the actual value was not
     equal to the expected value

因此,您阅读了该Returns消息并询问这怎么可能?

如果两个线程几乎同时调用incrementAndGet,并且它们都进入并看到 value current == 1。两个线程都将创建一个线程本地next == 2并尝试通过compareAndSet. 根据记录,只有一个线程会获胜,失败的线程必须重试。

这就是 CAS 的工作原理。如果失败,您尝试更改值,再试一次,如果成功则继续。

现在简单地将字段声明为 volatile 将不起作用,因为递增不是原子的。因此,从我解释的情况来看,这样的事情并不安全

volatile int count = 0;

public int incrementAndGet(){
   return ++count; //may return the same number more than once.
}
于 2013-06-05T13:12:28.820 回答
1

Java 的 compareAndSet 基于 CPU 比较和交换 (CAS) 指令,请参见http://en.wikipedia.org/wiki/Compare-and-swap它将内存位置的内容与给定值进行比较,并且仅当它们相同时,才将该内存位置的内容修改为给定的新值。

在 incrementAndGet 的情况下,我们读取当前值并调用compareAndSet(current, current + 1). 如果它返回 false 则意味着另一个线程干扰并更改了当前值,这意味着我们的尝试失败了,我们需要重复整个循环直到它成功。

于 2013-06-05T13:28:00.350 回答