为什么atomic提供compare_exchange_strong?

时间:2016-06-08 09:59:58

标签: java atomic

现在我正在研究Android上的AtomicInteger课程。

这个Java类有两个方法

public final void set(int newValue) {
    value = newValue;
}

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

问题1: set方法直接存储新值,所以这个方法不是原子的?如果我们希望在许多线程中结果是正确的,我们需要使用compareAndSet方法吗?

我已阅读有关AtomicInteger.compareAndSet()的源代码。最后它调用std::atomic<T>::compare_exchange_strong方法。 incrementAndGet方法调用compareAndSet。

问题2: 我读过一些关于CAS的文章,compare_exchange_strong()是原子的,store()也是原子的。所以我想知道,为什么我们不直接使用商店,为什么需要CAS方法?

1 个答案:

答案 0 :(得分:1)

CAS的要点是它允许您在原始值上存储条件的值。当然,一个简单的商店可以原子方式完成,但它可能不是你想要的。

考虑简单的“原子增量”操作:

  1. 加载当前值。
  2. 计算新值。
  3. 存储新值。
  4. 如果您为此过程使用了不相关的原子加载和存储,那么两个线程可以读取,计算和存储相同的值,因此其中一个增量会丢失!加载和存储是简单的原子操作,而CAS是原子读 - 修改 - 写(RMW)操作。 RMW比简单的装载和存储更复杂。 RMW不仅保证正确生成简单的值,而且正确应用复杂的逻辑操作。

    让我们使用CAS实现原子增量来演示它是如何工作的:

    // Effect: x += n, returns old value of x.
    int inc(int n, std::atomic<int> & x)
    {
        int old_val = x.load();
        for (;;) {
            int new_val = old_val + n;
            if (x.compare_exchange_weak(old_val, new_val)) {
                return old_val;
            }
            // Note: If the exchange fails, old_val is updated
            //       to the current value of x.
        }
    }
    

    这里的关键点是这个操作必须是一个循环,因为当我们计算我们的暂定结果(new_val = old_val + n)时,值old_val可能已经过时,因为其他一些线程已被修改它。所以我们必须循环,直到我们有机会在x当前保留旧值的情况下应用新值。这就是CAS的要点:它以我们认为的旧值为条件存储一个新值。

    关于“强”与“弱”交换的区别:如果你希望你的算法无条件地成功,你总是想要一个像我所展示的循环,你使用弱交换。不同之处在于弱形式可能“虚假地失败”,即返回false,即使存储的值是预期值。 (这种放松允许在某些平台上提供更有效的指令。)强形式可能更昂贵但不会虚假失败,并且在您执行“乐观”CAS但不关心它是否成功的情况下它可能很有用。这在某些并发数据结构(例如队列)中很有用,其中线程尽最大努力帮助,但不需要保证成功。