【问题标题】:Difference between getAndSet and compareAndSet in AtomicBooleanAtomicBoolean 中 getAndSet 和 compareAndSet 的区别
【发布时间】:2015-03-24 17:17:23
【问题描述】:

线程标题应该是不言自明的......我对AtomicBoolean类的以下方法的规范有点困惑:

  • java.util.concurrent.atomic.AtomicBoolean#compareAndSet
  • java.util.concurrent.atomic.AtomicBoolean#getAndSet

我的假设是,当在 if 条件中用作布尔子句时,两者都会导致相同的行为:

public class Test {
  private AtomicBoolean flag = AtomicBoolean(false);

  public void processSomeAction() {
    if (flag.getAndSet(false)) { // Shouldn't this be similar to flag.compareAndSet(false)
      // process some action
    }
  }
  //...
  private void internalMutatorMethod() {
    // do some staff then update the atomic flag
    flas.set(true);
  }
}

假设我想检索当前标志值并自动更新它,这两种方法不应该产生相同的行为吗?

如果我遗漏了内部差异,我将非常感谢有关如何以及何时使用它们的任何解释。

【问题讨论】:

  • compareAndSet 有两个参数。 javadoc 表明它与getAndSet 完全不同。 - 您可以编写与 getAndSet 相同的 compareAndSet 调用,但这几乎不值得提问。

标签: java synchronization atomic atomicboolean


【解决方案1】:

documentation 非常清楚。

  • getAndSet --> "以原子方式设置为给定值并返回之前的值。"
  • compareAndSet --> "如果当前值 == 预期值,则自动将值设置为给定的更新值。"

毫不奇怪,compareAndSet 接受两个参数。

在您的具体情况下:

  • if (flag.getAndSet(false)) 将设置 flagfalse 仅当其先前的值为 true
  • 相当于if (flag.compareAndSet(true, false))

【讨论】:

  • 是的,文档很清楚,正如您所建议的那样,我可以看到总体上的差异......但我正在寻求根据我在主线程中编写的代码 sn-p 来查看差异.那里有什么不同吗?
  • 这正是我期待确认的......在我的情况下它们应该是等价的:)
  • @tmarwen 你错误地假设 compareAndSet 只接受一个参数。
  • "if (flag.getAndSet(false)) 只有在 flag 之前的值为 true 时才会将 flag 设置为 false" - 这是不正确的。 getAndSet() 总是将值设置为 ,无论之前的值是真还是假。
【解决方案2】:

您可以查看代码以更好地理解:

public final boolean getAndSet(boolean newValue) {
    for (;;) {
        boolean current = get();
        if (compareAndSet(current, newValue))
            return current;
    }
}

getAndSet 中,如果布尔值在您get() 旧值和您尝试更改其值之间发生变化,compareAndSet 不会更改其值。因此,getAndSet 在循环中调用compareAndSet,直到布尔值设置为新值。

至于您的代码示例:

flag.getAndSet(false) 返回 AtomicBoolean 的旧值。另一方面,flag.compareAndSet(x,false)(注意有两个参数)返回 AtomicBoolean 是否被修改,或者换句话说,它返回 AtomicBoolean 的旧值是否为 x。

【讨论】:

    【解决方案3】:

    当我检查了我发现的实现时

    public final boolean getAndSet(boolean newValue) {
        for (;;) {
            boolean current = get();
            if (compareAndSet(current, newValue))
                return current;
        }
    }
    

    同样在检查 javadoc 时,compareAndSet 仅在比较通过时设置值,而 getAndSet 只需设置值并返回前一个值。

    【讨论】:

    • 这仅适用于早期版本的 JDK。 JDK 8 将其重写为 do-while 循环,JDK 9 到 JDK 14 使用VarHandle 实现,即native
    【解决方案4】:

    线程有点老了,但是没有人提到getAndSet会比compareAndSet更高效。 CAS 是一个非常昂贵的指令(在所有 CPU 架构上,因此 JVM 在这里无关紧要)。所以它们并不完全等价。

    所以关于 OP,这两种方法都会产生相同的行为,但不会有相同的性能,请尽可能使用 getAndSet。

    【讨论】:

    • 显然这不是真的,尤其是在谈论 lock cmpxchg: stackoverflow.com/questions/5339769/… 时。此外,与它对其他内核的影响相比,该指令本身的成本并不高:可能会迫使它们再次从主存中读取。
    • 你能找到相关的部分吗?它们都是缓慢的指令,因为确实存在内存锁定。但是(锁定)xchg 至少在最近的英特尔拱门上仍然获胜(agner.org/optimize/instruction_tables.pdf)。 cmpxchg 速度较慢,因为它的定义更复杂,需要更多微操作。
    猜你喜欢
    • 1970-01-01
    • 2016-07-25
    • 2015-03-25
    • 2017-02-19
    • 1970-01-01
    • 1970-01-01
    • 2011-10-12
    • 2011-02-17
    • 2016-10-18
    相关资源
    最近更新 更多