for循环的每个循环都是原子操作吗?

Is each cycle of a for loop an atomic operation?

下面的AtomicBigInteger实现的incrementAndGet方法是原子操作吗? 我特别想知道 for (; ; ) 部分。 JVM 是否以某种方式保证 for 循环中的每个循环都是原子执行的?

public final class AtomicBigInteger {

    private final AtomicReference<BigInteger> valueHolder = new AtomicReference<>();

    public AtomicBigInteger(BigInteger bigInteger) {
        valueHolder.set(bigInteger);
    }

    public BigInteger incrementAndGet() {
        for (; ; ) {
            BigInteger current = valueHolder.get();
            BigInteger next = current.add(BigInteger.ONE);
            if (valueHolder.compareAndSet(current, next)) {
                return next;
            }
        }
    }
}

我从这里得到这段代码: Possible to safely increment BigInteger in a thread safe way, perhaps with AtomicReference, w/o locking? 然而,这个实现正在流行,您可以在互联网上的许多不同地方找到它。

不,它不是原子的,但如果另一个线程修改了 AtomicReference,那么 compareAndSet 调用将失败,它将再次循环,获取值,递增它,然后尝试再次设置它。迟早(可能)它会成功并将 AtomicReference 持有的 BigInteger 更新为下一个数字。

您 class 中的方法 incrementAndGet 将不是原子的。原因如下。

Atomic* class 使用 volatile 值引用。这些值的内存偏移量也保存在实例中,它们可以使用这些实例 fetch-increment-compare-set 在循环中 直到当前线程能够执行一次完成所有操作(即,没有另一个线程在其间执行增量)。

这对这些 Atomic* 来说是可能的,正如我所见,由于内在的“可信”classes 必须访问 Unsafe 实现。 Unsafe 实现具有使用 native 函数自动 比较和设置 的方法。

在您提到的情况下,我们将不得不求助于使用 synchronized 块,其等效的基于 Lock 的实现,或者简单地使用 AtomicReference 中的方法。像这样:

public class AtomicBigInteger{
    private final AtomicReference<BigInteger> valueHolder = new AtomicReference<>();

    public AtomicBigInteger(BigInteger bigInteger) {
        valueHolder.set(bigInteger);
    }

    public BigInteger incrementAndGet() {
        return valueHolder.updateAndGet( bigInt -> bigInt.add( BigInteger.ONE ) );
    } 
}

但是,由于我们正在处理 BigInteger,因此也必须审查此实现,因为 AtomicReference.updateAndGet(..) 可能必须执行的迭代次数可能很重要,因为 BigInteger.add( BigInteger ) 涉及很多步骤,不像添加两个 ints.