【问题标题】:Is each cycle of a for loop an atomic operation?for 循环的每个循环都是原子操作吗?
【发布时间】:2021-12-20 10:38:21
【问题描述】:

下面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? 但是,此实现正在流行,您可以在互联网上的许多不同地方找到它。

【问题讨论】:

  • 你应该链接到这段代码的源代码。

标签: java atomic atomicinteger


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

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

    这对于这些Atomic* 来说是可能的,正如我所见,由于内在的“受信任”类对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。

    【讨论】:

    • 我的问题的两个答案都非常有帮助。我接受你的,因为你提供了一个看起来更好的替代实现。我已经通过并行运行大量请求成功地测试了您的实现的正确性。我注意到,您的实现甚至更高效。
    • 不错。一个不错的补充是使用 BigInteger.ZERO 初始化的无参数构造函数。
    猜你喜欢
    • 2014-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-11
    • 2021-08-27
    • 1970-01-01
    • 2019-08-23
    相关资源
    最近更新 更多