为什么AtomicInteger和类似类的getAndSet()中有一个循环?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (28)

在这段代码中使用循环是为了什么目的?

public final int getAndSet(int newValue) {
    for (;;) {
        int current = get();
        if (compareAndSet(current, newValue))
            return current;
    }
}
提问于
用户回答回答于

AtomicXXX类表示原子数据类型。这意味着当两个或多个线程并发访问时,它们必须返回一致的结果。

compareAndSet是通常在硬件中直接实现的操作,因此getAndSet是根据compareAndSet

该方法的工作方式如下:首先,返回当前值。

现在,可能会有另一个线程并发地更改该值,因此必须使用compareAndSet但事实并非如此。如果另一个线程更改了该值,则必须重复该过程,否则将返回错误的值。因此产生了循环。

用户回答回答于

CPU指令称为比较与设定(或CAS(简言之)旨在帮助这方面的工作,实际上是这样做的:

if (value == providedValue) {
  value = newValue;
  return true;
} else {
  return false;
}

这些指令可以在机器代码级别执行,并且比创建锁要快得多.

想象一下你想要添加1使用这些指令之一的数字,其方式将在高度并行负载下始终正确工作。显然,可以将其编码为:

int old = value;
if ( compareAndSet(old, old+1) ) {
  // It worked!
} else {
  // Some other thread incremented it before I got there.
}

但如果CAS失败了?你猜到了-再试一次!

boolean succeeded = false;
do {
  int old = value;
  if ( compareAndSet(old, old+1) ) {
    // It worked!
    succeeded = true;
  } else {
    // Some other thread incremented it before I got there. Just try again.
  }
} while (!succeeded);

在这里你可以看到你观察到的模式。

使用这个和类似的习惯用法,就可以使用完全不使用锁(通常称为“锁”)来实现许多函数,甚至实现一些相当复杂的数据结构。无锁)。

扫码关注云+社区