AtomicInteger weakCompareAndSet上的“虚假故障”是什么意思?

2022-09-02 03:31:20

Java AtomicInteger 类有一个方法 -

boolean weakCompareAndSet(int expect,int update)

它的附言说:

可能会虚假失败。

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


答案 1

虚假:无明显原因

根据原子包javadoc:

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

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

false return 仅意味着可以根据需要重试该操作,这取决于以下保证:当变量保持 expectedValue 并且没有其他线程也尝试设置变量时,重复调用最终将成功。
(例如,这种虚假故障可能是由于内存争用效应引起的,这些效应与预期值和当前值是否相等无关。

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


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

weakCompareAndSet 以原子方式将值设置为给定的更新值(如果当前值 == 预期值)。可能会虚假失败。

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

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

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


关于订购前发生的注意事项

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

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

跨线程的发生之前排序只能通过在公共锁上进行同步或访问公共易失性变量来创建。

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


答案 2

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

换句话说,该方法可能什么都不做,并且无缘无故地返回 false...
在某些CPU架构中,这可能比强力具有性能优势。CompareAndSet()


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

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

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

推荐