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

ezt*_*tam 10 java atomic atomicinteger

incrementAndGet以下 AtomicBigInteger 实现的方法是原子操作吗?我特别想知道这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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我从这里得到了这段代码: Maybe to secureincrement BigInteger in a thread safe way, 也许使用 AtomicReference, w/olocking? 然而,这种实现正在广泛传播,您可以在互联网上的许多不同地方找到它。

Dav*_*rad 10

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


Sre*_*mar 10

你的类中的方法incrementAndGet将不是原子的。原因如下。

这些Atomic*类使用volatile值引用。这些值的内存偏移量也保存在实例内,使用它们能够在循环中获取增量比较集,直到当前线程能够一次性完成所有操作(即,没有另一个线程在之间)。

正如我所看到的,这对于这些来说是可能的Atomic*,因为内在的“可信”类可以访问实现Unsafe。这些Unsafe实现具有使用函数进行原子比较和设置的native方法。

在像您提到的情况下,我们将不得不求助于 usingsynchronized块、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 ) );
    } 
}
Run Code Online (Sandbox Code Playgroud)

然而,由于我们正在处理,因此也必须审查此实现,因为可能需要执行的BigInteger迭代次数可能很大,因为涉及很多步骤,与添加两个s 不同。AtomicReference.updateAndGet(..)BigInteger.add( BigInteger )int