Java AtomicInteger 类有一个方法 -
boolean weakCompareAndSet(int expect,int update)
它的文档说:
可能会虚假失败。
这里的“虚假失败”是什么意思?
虚假地:没有明显的原因
根据atomic
包javadoc:
原子类也支持方法weakCompareAndSet,它的适用性有限。
在某些平台上,弱版本在正常情况下可能比 compareAndSet 更有效,但不同之处在于,任何给定的 weakCompareAndSet 方法调用都可能虚假地返回 false(也就是说,没有明显的原因)。
错误返回仅意味着可以在需要时重试操作,这依赖于当变量保持预期值并且没有其他线程也尝试设置变量时重复调用最终会成功的保证。
(例如,这种虚假故障可能是由于与预期值和当前值是否相等无关的内存争用效应造成的。)此外,weakCompareAndSet 不提供同步控制通常需要的排序保证。
根据这个线程,这不是因为“硬件/操作系统”,而是因为 weakCompareAndSet 使用的底层算法:
如果当前值 == 预期值,weakCompareAndSet 会自动将值设置为给定的更新值。可能会虚假失败。
与 compareAndSet() 和 AtomicX 上的其他操作不同,weakCompareAndSet() 操作不会创建任何先发生顺序。
因此,仅仅因为线程看到由weakCompareAndSet 引起的AtomicX 更新并不意味着它与weakCompareAndSet() 之前发生的操作正确同步。
您可能不想使用此方法,而应该只使用 compareAndSet; 因为在少数情况下weakCompareAndSet 比compareAndSet 更快,并且在许多情况下,尝试使用weakCompareAndSet 而不是compareAndSet 来优化代码会在代码中引入微妙且难以重现的同步错误。
关于发生前订购的注意事项:
Java 内存模型 (JMM) 定义了保证读取变量的线程在另一个线程中看到写入结果的条件。
JMM 定义了一个名为happens-before 的程序的操作顺序。
Happens-before 跨线程的排序只能通过在公共锁上同步或访问公共 volatile 变量来创建。
在没有发生之前的顺序的情况下,Java 平台有很大的自由度来延迟或更改一个线程中的写入对另一个线程中相同变量的读取可见的顺序。
这意味着即使它当前包含预期值,它也可能返回 false(并且不会设置新值)。
换句话说,该方法可能什么都不做,并且没有明显的原因返回 false……
在某些 CPU 架构中,这可能比强CompareAndSet()
.
关于为什么会发生这样的事情的更具体的细节。
一些架构(如较新的 ARM)使用加载链接 (LL)/条件存储 (SC) 指令集实现 CAS 操作。LL 指令将值加载到内存位置并在某处“记住”该地址。如果记忆地址处的值未被修改,则 SC 指令将一个值存储到该内存位置。硬件有可能认为该位置已被修改,即使它显然没有出于多种可能的原因(原因可能因 CPU 架构而异):
weakCompareAndSet 的一个很好的用例是性能计数器 - 无需排序,更新率高(因此排序对弱排序系统造成伤害),但不会在高负载下减少计数(紧密满足的性能计数器可以减少 99%计数,基本上使计数器的值相对于非竞争计数器随机)。