如何在java.util.concurrent.atomic包中定义的类中实现原子性



我正在浏览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(中是否有任何特殊用途?请帮我找到一个描述性的理解。提前谢谢。

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

此代码的目的是确保正确更新volatile字段,而不会产生synchronized锁的开销。 除非有大量线程都在竞争更新同一字段,否则这很可能会旋转几次来完成此操作。

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

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

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

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

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

public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

我无法理解编写序列的目的 无限 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,并且它们都输入并查看值current == 1 。 两个线程都将创建一个线程本地next == 2并尝试通过 compareAndSet 进行设置。 根据记录,只有一个线程会获胜,失败的线程必须重试。

这就是 CAS 的工作方式。 如果失败,则尝试更改值,请重试,如果成功,请继续。

现在,简单地将字段声明为易失性是行不通的,因为递增不是原子的。 所以这样的事情从我解释的场景中是不安全的

volatile int count = 0;
public int incrementAndGet(){
   return ++count; //may return the same number more than once.
}

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

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

最新更新