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

时间:2013-06-05 13:00:45

标签: java concurrency thread-safety atomic java.util.concurrent

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

3 个答案:

答案 0 :(得分: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。
  10.   

    它是否在Java Memory Model(JMM)中用于任何特殊目的。

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

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

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

答案 1 :(得分:6)

  

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

要理解为什么它处于无限循环中,我发现理解compareAndSet的作用以及它如何返回错误会有所帮助。

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将无效,因为递增不是原子。因此,我所解释的场景 不安全

volatile int count = 0;

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

答案 2 :(得分:1)

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

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