AtomicBoolean做了哪些volatile boolean不能做到的事情?


当前回答

这里的很多答案都过于复杂,令人困惑,或者是错误的。例如:

如果你有多个线程修改布尔值,你应该使用AtomicBoolean。

一般来说,这是不正确的。

如果一个变量是volatile,那么对它的每个原子访问都是同步的……

这是不正确的;同步完全是另一回事。

简单的答案是,AtomicBoolean允许您在某些操作中防止竞争条件,这些操作需要读取值,然后根据所读取的内容写入值;它使这些操作具有原子性(即它删除了变量可能在读和写之间发生变化的竞态条件)——因此得名。

如果你只是读写变量,写操作并不依赖于你刚刚读取的值,volatile就可以很好地工作,即使是多线程。

其他回答

记住成语——

READ - MODIFY- WRITE这个你不能用volatile实现

AtomicBoolean有一些方法可以原子地执行复合操作,而不必使用同步块。另一方面,volatile布尔值只能在同步块中执行复合操作。

读取/写入volatile boolean的内存效果分别与AtomicBoolean的get和set方法相同。

例如,compareAndSet方法将自动执行以下操作(没有同步块):

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

因此,compareAndSet方法将允许您编写保证只执行一次的代码,即使是从多个线程调用时也是如此。例如:

final AtomicBoolean isJobDone = new AtomicBoolean(false);

...

if (isJobDone.compareAndSet(false, true)) {
    listener.notifyJobDone();
}

保证只通知侦听器一次(假设没有其他线程将AtomicBoolean设置为true后再次设置为false)。

这里的很多答案都过于复杂,令人困惑,或者是错误的。例如:

如果你有多个线程修改布尔值,你应该使用AtomicBoolean。

一般来说,这是不正确的。

如果一个变量是volatile,那么对它的每个原子访问都是同步的……

这是不正确的;同步完全是另一回事。

简单的答案是,AtomicBoolean允许您在某些操作中防止竞争条件,这些操作需要读取值,然后根据所读取的内容写入值;它使这些操作具有原子性(即它删除了变量可能在读和写之间发生变化的竞态条件)——因此得名。

如果你只是读写变量,写操作并不依赖于你刚刚读取的值,volatile就可以很好地工作,即使是多线程。

两者都是相同的概念,但在原子布尔中,它将为操作提供原子性,以防cpu切换发生在两者之间。

Volatile boolean vs AtomicBoolean

Atomic*类包装了相同类型的volatile原语。来源:

public class AtomicLong extends Number implements java.io.Serializable {
   ...
   private volatile long value;
   ...
   public final long get() {
       return value;
   }
   ...
   public final void set(long newValue) {
       value = newValue;
   }

如果你所做的只是获取和设置一个Atomic*,那么你也可以用一个volatile字段来代替。

AtomicBoolean做了哪些volatile boolean不能做到的事情?

原子类为您提供了提供更高级功能的方法,例如用于数字的incrementAndGet(),用于布尔值的compareAndSet(),以及其他实现多重操作(get/increment/set, test/set)而无需锁定的方法。这就是为什么Atomic*类如此强大。

例如,如果多个线程使用++使用下面的代码,就会出现竞争条件,因为++实际上是:get、increment和set。

private volatile value;
...
// race conditions here
value++;

然而,下面的代码将在多线程环境中安全工作,没有锁:

private final AtomicLong value = new AtomicLong();
...
value.incrementAndGet();

同样重要的是要注意,从对象的角度来看,使用Atomic*类包装volatile字段是封装关键共享资源的好方法。这意味着开发人员不能假设字段不共享就处理它,可能会在字段++中注入问题;或其他引入竞态条件的代码。