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 )
涉及很多步骤,不像添加两个 int
s.
下面的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 )
涉及很多步骤,不像添加两个 int
s.