繁体   English   中英

原子值可以在“&&”操作期间更改吗?

[英]Can Atomic values change during an "&&" operation?

我知道下一个场景:(奇怪的格式,我知道)

private final AtomicBoolean aBoolean = new AtomicBoolean(true);

public void doSomething() {
    if (
        aBoolean.get()                       // line A
            &&                               // line B
        aBoolean.compareAndSet(true, false)  // line C
        ) {
        System.out.println("Was true!")
    }
}

如果线程#1 和线程#2 同时进入doSomething() ,这将发生:

  1. 线程 #1 和线程 #2 将同时读取aBoolean.get()为 == "true"。

  2. 两者都将执行“&&”运算符。

  3. CMPXCHG 指令同时为两个线程启动:

    3.1 本地使用LOCK前缀

    3.2 线程#1 或#2 先到达,赢得比赛。

    3.3 获胜线程比较(aBoolean == true?)这将返回“true”,因此 aBoolean 将设置为“false”。

    3.4 aBoolean 现在是假的。

    3.5 丢失线程比较(aBoolean == true?)这将返回“false”,因此会短路任何进一步的操作。

  4. 获胜线程将打印“是真的。”。

从“失败”线程的角度来看,“A 行”中的第一个aBoolean.get()是......比方说......一个“谎言”。

现在假设执行可以发生在操作符之间,就像上面例子中所做的那样,让我们为第二种情况添加第二种方法:

public void unluckySet() {
    aBoolean.set(false);
}

假设线程 #3 恰好在我们的“获胜线程”到达执行“&&”的“B 行”的确切时刻到达并执行unluckySet()

如果获胜线程到达“B 行”,则意味着它到达“A 行”,aBoolean 为“真”。

我的问题是:

CMPXCHG 是否会将更新后的值正确读取为“假”?这意味着.set()也由与compareAndSet()相同的锁持有。

在并发和线程之间:

运算符(“&&”、“||”、“==”、“=”,甚至可能是“return;”?以交错方式结束,防止可能的冲突?

Java memory model 是在没有数据竞争(您的程序没有)的情况下的顺序一致性 这是非常强大的; 它说你程序中的所有读写形成一个总顺序,这与程序顺序一致。 因此,您可以想象来自不同线程的读取和写入只是以某种方式交错或混洗在一起 - 而不会改变从同一线程执行的操作的相对顺序。

但是为了这个目的,每个动作都是这个顺序中的一个单独的元素。 因此,仅仅因为aBoolean.get()aBoolean.compareAndSet()两个动作而不是一个动作,其他线程的任意数量的其他动作都可能发生在它们之间。

这些动作是单个语句的一部分还是不同语句的一部分都没有关系; 或者他们以什么样的表情出现; 或者他们之间有什么运营商(如果有的话); 或者线程本身可能会或可能不会围绕它们进行哪些计算。 没有办法让两个动作“如此接近”,以至于在两者之间不会发生任何其他事情,除非用语言定义为原子的单个动作替换它们。


在机器级别,一个非常简单的方法是,由于aBoolean.get()aBoolean.compareAndSet()几乎可以肯定是两条不同的机器指令,因此它们之间可能会出现中断。 这个中断可能导致线程被延迟任意时间,在此期间其他线程可以做任何他们想做的事情。 因此,线程#1 和#2 完全有可能在它们的get()compareAndSet()之间都被中断,而线程#3 同时执行它的set

注意:推理特定机器可能如何工作通常有助于理解为什么可能出现不希望的行为,如上一段所述。 但它不能代替正式的 memory model 的推理,也不应该用来试图论证程序必须具有其所需的行为。 即使你心目中的一台特定机器会为你的代码做正确的事情,或者你想不出一个看似合理的机器会失败的方式,但这并不能证明你的程序是正确的。

因此,试图说“哦,机器会lock cmpxchg等等等等等等,一切正常”是不明智的; 您没有想到的其他一些机器可能会以完全不同的方式工作,它仍然符合抽象 Java memory model 但在其他方面违反了您的基于 x86 的期望。 事实上,x86 是一个特别差的例子:由于历史原因,它提供了一组相当强大的指令级 memory 排序保证许多其他“弱排序”架构没有,因此可能有很多东西 ZD523187880E1EA223 抽象允许,但实际上 x86 不会这样做。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM