19

源代码是一样的。

public final boolean compareAndSet(V expect, V update) {
    return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

public final boolean weakCompareAndSet(V expect, V update) {
    return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

重点是什么?

4

4 回答 4

18

在 x86 上,LOCK CMPXCHG 指令用于实现 CAS。它是原子的,提供(接近)最大排序保证并且不会遭受虚假故障。因此,在 x86 平台上,保证较少的 CAS 没有任何好处。

但是在其他平台上,例如 PowerPC 或 ARM(没有 LSE 扩展),CAS 是作为一系列指令实现的,这些指令提供LL/SC行为和内存屏障作为单独的构建块。这为您的 CAS 在排序和故障保证方面的强大程度创造了一些回旋余地。相反,这意味着全强度 CAS 的指令序列可能比某些并发算法所需的成本更高。

许多并发算法涉及循环,当 CAS 失败时重试或重新计算操作,然后重试。由于 LL/SC 可能会虚假地失败,因此基于它的强大 CAS 实现必须在内部循环。如果代码已经包含外循环,则可以通过将强 CAS 替换为允许虚假失败的弱 CAS 来避免内循环。

因此,weakCAS 的存在是为了在弱排序架构上实现更高效的代码。

javadoc 对弱化排序的确切含义含糊不清,因为它目前无法用 java 内存模型表示。这可能会在将来与 C++11 内存模型更紧密地保持一致时进行修改。

JSR-133 Cookbook的多处理器章节中的表格概述了平台的不同之处。

于 2016-04-05T14:55:48.580 回答
12

weakCompareAndSet javadoc是这样解释的:

如果当前值 == 预期值,则自动将值设置为给定的更新值。

可能会错误地失败并且不提供排序保证,因此很少是 compareAndSet 的合适替代方案。

简而言之,javadoc 是说该weak版本是(或曾经是)提供“较弱”保证的版本。

现在,正如您所观察到的,这两种方法的当前实现是相同的。根据 Grepcode 站点上的源代码,从 Java 6 到 Java 8(至少)都是如此。

所以我推测这两种方法的实现是:

  • 最初不同,但由于对以下实施的大修而做出了相同的结果Unsafe

    • 为了权宜之计(例如,为了节省实施工作
    • 因为“弱”版本的假定性能,或
    • 因为“弱”版本是有问题的;例如,它很难正确使用。
  • 最初相同,并且指定了差异(但未实现),因为设计人员认为可能存在性能优势。

最后一种解释不太可能。如果两个方法最初实现相同,重新实现它们不同将有破坏预先存在的代码的风险。这是一个坏主意,即使对于Unsafe.


@assylias / @ Stefan Gobel 评论了另一种解释。基本上,我们在源代码中看到的“相同代码”实际上可能会被 JIT 编译器重写,从而为两种方法提供不同的机器代码。

这当然是有道理的。JIT 编译器确实为某些(非本机)方法调用生成了特殊情况代码:所谓的“内在”。


在 Java 9 中,该weakCompareAndSet方法被标记为已弃用。源码中的解释是:

此方法具有普通的记忆效应,但方法名称暗示易失性记忆效应(请参阅 {@link #compareAndExchange} 和 {@link #compareAndSet} 等方法)。为避免混淆普通或易失​​性记忆效应,建议改用 {@link #weakCompareAndSetPlain} 方法。

另一方面,我们现在看到它compareAndSet的实现方式与weakCompareAndSet/不同weakCompareAndSetPlain

public final boolean compareAndSet(V expectedValue, V newValue) {
    return VALUE.compareAndSet(this, expectedValue, newValue);
}

public final boolean weakCompareAndSet(V expectedValue, V newValue) {
    return VALUE.weakCompareAndSetPlain(this, expectedValue, newValue);
}

whereVALUE被声明为 a java.lang.invoke.VarHandle。上面使用的VarHandle方法被native标记为内在候选方法。

于 2016-04-05T13:56:02.797 回答
1

同样来自java docs,看起来其他答案错过了这个:

原子类也支持方法weakCompareAndSet,它的适用性有限。在某些平台上,弱版本在正常情况下可能比 compareAndSet 更有效,但不同之处在于,任何给定的 weakCompareAndSet 方法调用都可能虚假地返回 false(也就是说,没有明显的原因)。错误返回仅意味着可以在需要时重试操作,这依赖于当变量保持预期值并且没有其他线程也尝试设置变量时重复调用最终会成功的保证。(例如,这种虚假故障可能是由于与预期值和当前值是否相等无关的内存争用效应造成的。) 此外,weakCompareAndSet 不提供同步控制通常需要的排序保证。然而,当这些更新与程序的其他发生前发生的顺序无关时,该方法对于更新计数器和统计数据可能是有用的。当线程看到由weakCompareAndSet 引起的原子变量更新时,它不一定看到在weakCompareAndSet 之前发生的任何其他变量的更新。例如,在更新性能统计信息时,这可能是可以接受的,但很少会这样。它不一定会看到在weakCompareAndSet 之前发生的任何其他变量的更新。例如,在更新性能统计信息时,这可能是可以接受的,但很少会这样。它不一定会看到在weakCompareAndSet 之前发生的任何其他变量的更新。例如,在更新性能统计信息时,这可能是可以接受的,但很少会这样。

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html#weakCompareAndSet

于 2018-08-05T10:59:42.517 回答
-1

从功能上讲,两者是相同的。主要区别在于 weakAtomicCompareAndSet 可能会虚假失败(请参阅 oracle 文档)并且不提供排序保证

推荐使用 atomicCompareAndSet 而不是弱版本

于 2016-04-05T13:43:44.783 回答