AtomicInteger weakCompareAndSet上的“虚假失败”是什么意思?

Java AtomicInteger类有一个方法 –

boolean weakCompareAndSet(int expect,int update) 

它的文件说:

可能是虚假的失败。

这里“虚假失败”是什么意思?

虚假地:没有明显的理由

根据atomic包javadoc:

primefaces类还支持weakCompareAndSet方法,其适用性有限。

在某些平台上,弱版本在正常情况下可能比compareAndSet更有效,但不同之处在于, 任何给定的weakCompareAndSet方法调用都可能虚假地返回false (也就是说, 没有明显的原因 )。

假返回仅表示如果需要可以重试操作,依赖于当变量保持expectedValue时没有其他线程也尝试设置变量的重复调用的保证最终会成功。
(例如,这种虚假故障可能是由于与预期值和当前值相等无关的内存争用效应引起的。)

另外,weakCompareAndSet不提供同步控制通常需要的排序保证。


根据这个线程 ,它不是因为“硬件/操作系统”,而是因为weakCompareAndSet使用的底层算法:

如果当前值==期望值,则weakCompareAndSet以primefaces方式将值设置为给定的更新值 。 可能是虚假的失败。

与compareAndSet()和AtomicX上的其他操作不同, weakCompareAndSet()操作不会创建任何先前发生的排序

因此,仅仅因为线程看到由weakCompareAndSet引起的对AtomicX的更新并不意味着它与weakCompareAndSet()之前发生的操作正确同步。

您可能不想使用此方法,而应该只使用compareAndSet; 因为很少有情况下weakCompareAndSet比compareAndSet快,并且在许多情况下,尝试使用weakCompareAndSet而不是compareAndSet来优化代码会导致细微且难以在代码中重现同步错误。


有关发生前的订单的注意事项:

Java内存模型(JMM)定义了读取变量的线程保证在另一个线程中看到写入结果的条件。

JMM定义了一个名为happen-before的程序操作的顺序。

发生在线程之前的排序只能通过同步公共锁或访问公共volatile变量来创建。

在没有发生排序之前,Java平台具有很大的自由度来延迟或改变一个线程中的写入对于另一个线程中的同一变量的读取可见的顺序。

这意味着即使它当前包含期望值,它也可能返回false(并且不会设置新值)。

换句话说,该方法可能无所作为,并且没有明显的原因返回false …
有一些CPU架构,这可能比强大的CompareAndSet()具有性能优势。


关于为什么会发生这样的事情的更具体细节。

某些体系结构(如较新的ARM)使用加载链接(LL)/存储条件(SC)指令集实现CAS操作。 LL指令将值加载到内存位置并在某处“记住”该地址。 如果尚未修改记忆地址处的值,则SC指令将值存储到该存储器位置。 硬件有可能认为位置已经被修改,即使它显然没有多种可能的原因(原因可能因CPU架构而异):

  1. 该位置可能已写入相同的值
  2. 观看的地址的分辨率可能不完全是感兴趣的一个内存位置(想想缓存行)。 写入“附近”的另一个位置可能会导致硬件将有问题的地址标记为“脏”
  3. 许多其他可能导致CPU丢失LL指令保存状态的原因 – 上下文切换,缓存刷新或页表更改可能。

weakCompareAndSet的一个很好的用例是性能计数器 – 无需订购,更高的更新率(因此在弱序系统上排序会受到伤害),但在高负载下不会丢失计数(紧密满足的性能计数器可能会下降99%)计数,基本上将计数器的值相对于非竞争计数器随机保留。

但为什么允许这种情况发生呢? 这是因为下面的硬件/操作系统是错误的吗? 或者这背后有一些很好的技术原因?