gpt4 book ai didi

java - compareAndSet 不成功操作的内存效应

转载 作者:搜寻专家 更新时间:2023-11-01 03:36:37 31 4
gpt4 key购买 nike

Java 通过其原子类公开 CAS 操作,例如

boolean compareAndSet(expected,update)

JavaDocs指定 compareAndSet 操作的内存效果如下:

compareAndSet and all other read-and-update operations such as getAndIncrement have the memory effects of both reading and writing volatile variables.

这绝对适用于成功的 compareAndSet 调用。但是,如果 compareAndSet 返回 false,内存效应是否也成立?

我会说不成功的 compareAndSet 对应于 volatile 读取(因为在这种情况下必须访问原子实例的当前值),但我不明白为什么 CAS 应该执行不成功情况下的特殊内存屏障指令。

问题实际上是,不成功的 CAS 是否也建立了 happens-before 关系。考虑以下程序:

public class Atomics {
private static AtomicInteger ai = new AtomicInteger(5);
private static int x = 0;

public static void main(String[] args) {
new Thread(() -> {
while (x == 0) {
ai.compareAndSet(0, 0); // returns false
}
}, "T1").start();

new Thread(() -> {
x = 1;
ai.compareAndSet(0, 0); // returns false
}, "T2").start();
}
}

线程 T2(和程序)一定会终止吗?

最佳答案

使用 volatile 读取和写入建立happens-before 关系的问题在于,这种关系只存在于写入和后续 阅读。如果一个线程 T1 写入一个共享的 volatile 变量,而另一个线程 T2 从同一个变量读取,如果 T2 之前读取该变量,则不存在happens-before 关系T1 写信给它。如果决定 T1 是否在 T2 读取之前写入的只是线程调度,那么我们没有任何保证。

在没有额外同步的情况下处理它的一种实用方法是评估实际值,T2 已读取。如果此值表明 T1 已写入新值,则我们有一个有效的happens-before 关系。这就是它在使用 volatile boolean fooIsInitialized 标志或 volatile int currentPhase 计数器时的工作方式。很明显,如果写入的值与旧值相同,或者如果新值从未实际写入,这将无法工作。

您的示例程序的问题在于它推测线程调度。它假设 T2 最终执行了 cas 操作,并且在 T1 中将有一个后续迭代,其中下一个 cas 将创建一个先发生关系。但这并不能保证。这可能不是直觉上可以理解的,但如果没有同步,T1 的所有迭代都可能在 T2 的操作之前发生,即使循环是无限的。它甚至是一种有效的线程调度行为,让 T1 永远消耗 100% 的 CPU 时间,然后再将 CPU 时间分配给 T2,因为无法保证在相同优先级的线程之间进行抢占式线程切换。

但是,即使底层系统确实将 CPU 时间分配给最终会执行操作的 T2,JVM 也不需要让 T1 明白这一点,因为 T1 无法观察到 T2 曾经运行过。在现实生活中不太可能发现这一点,但答案仍然是没有保证。当存在一系列操作使 T1 可以观察到 T2 运行(即更改其状态)时,情况会发生变化,但当然,该操作链会使 cas 过时。

关于java - compareAndSet 不成功操作的内存效应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29846319/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com