首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >不成功操作的compareAndSet内存效应

不成功操作的compareAndSet内存效应
EN

Stack Overflow用户
提问于 2015-04-24 11:28:48
回答 2查看 384关注 0票数 5

Java通过其原子类公开CAS操作。

代码语言:javascript
复制
boolean compareAndSet(expected,update)

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

compareAndSet和所有其他读和更新操作(如getAndIncrement )都具有读取和写入易失性变量的内存效果。

对于成功的compareAndSet调用来说,这是绝对成立的。但是,如果compareAndSet返回false,内存效果也会保持不变吗?

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

问题实际上是,一个不成功的CAS是否也建立了一种发生之前的关系。考虑以下方案:

代码语言:javascript
复制
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 (和程序)肯定会终止吗?

EN

回答 2

Stack Overflow用户

发布于 2015-05-05 12:19:27

在使用volatile读写之前建立发生的关系的问题是,这种关系只存在于写和后续读。如果一个线程T1写入共享的volatile变量,而另一个线程T2从同一个变量读取,则不可能发生关系--如果T2在T1写入该变量之前读取该变量。如果确定T1在T2读取之前是否写入的所有内容都是线程调度,那么我们没有任何保证。

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

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

但是,即使底层系统确实将CPU时间分配给T2 (最终将执行这些操作),JVM也没有必要向T1说明这一点,因为T2运行的T1无法观察到这一点。在实际的实现中不太可能发现这一点,但答案仍然是没有保证。当有一系列操作使T1可以观察到T2运行时(即改变了其状态)时,事情就会发生变化,但是当然,这一系列的操作会使cas过时。

票数 1
EN

Stack Overflow用户

发布于 2015-05-05 14:23:30

我喜欢Holger的答案,而且由于技术信息的原因,我会接受这一点,但我会用相同的结果写出一个答案,但视角不同。这个程序有可能永远运行吗?是的,考虑可能的编译器重新排序。

代码语言:javascript
复制
    new Thread(() -> {
        if(x == 0){
            while (true) {
                ai.compareAndSet(0, 0); // returns false
            }
        }
    }, "T1").start();

这有可能是重新订购吗?是的,是这样的。没有规定说,这里的提升机不能发生,尽管随后的易失性商店。这里唯一的规则是读取不能在易失性存储下面执行。

编辑:我意识到问题是关于内存效果,而不是编译器排序。我会留下这个答案,因为它可能是有用的,但并没有真正回答问题。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29846319

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档