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


当前回答

Volatile关键字保证共享该变量的线程之间的happens-before关系。它不能保证2个或更多的线程在访问布尔变量时不会相互中断。

其他回答

Volatile关键字保证共享该变量的线程之间的happens-before关系。它不能保证2个或更多的线程在访问布尔变量时不会相互中断。

记住成语——

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

I use volatile fields when said field is ONLY UPDATED by its owner thread and the value is only read by other threads, you can think of it as a publish/subscribe scenario where there are many observers but only one publisher. However if those observers must perform some logic based on the value of the field and then push back a new value then I go with Atomic* vars or locks or synchronized blocks, whatever suits me best. In many concurrent scenarios it boils down to get the value, compare it with another one and update if necessary, hence the compareAndSet and getAndSet methods present in the Atomic* classes.

检查java.util.concurrent.atomic包中的JavaDocs,以获得Atomic类的列表和它们如何工作的出色解释(刚刚了解到它们是无锁的,因此它们比锁或同步块更有优势)

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字段是封装关键共享资源的好方法。这意味着开发人员不能假设字段不共享就处理它,可能会在字段++中注入问题;或其他引入竞态条件的代码。

它们完全不同。考虑下面这个易变整数的例子:

volatile int i = 0;
void incIBy5() {
    i += 5;
}

如果两个线程并发调用这个函数,i可能在后面是5,因为编译后的代码会有点类似于这个(除了你不能同步int):

void incIBy5() {
    int temp;
    synchronized(i) { temp = i }
    synchronized(i) { i = temp + 5 }
}

如果一个变量是volatile的,那么对它的每个原子访问都是同步的,但是并不总是很明显什么才是原子访问。使用Atomic*对象,可以保证每个方法都是“原子的”。

因此,如果使用AtomicInteger和getAndAdd(int delta),可以确保结果为10。以同样的方式,如果两个线程同时对一个布尔变量求反,使用AtomicBoolean可以确保它之后具有原始值,而使用volatile布尔则不能。

因此,每当有多个线程修改一个字段时,您需要使其原子化或使用显式同步。

volatile的用途是不同的。考虑这个例子

volatile boolean stop = false;
void loop() {
    while (!stop) { ... }
}
void stop() { stop = true; }

如果有一个线程运行loop(),另一个线程调用stop(),如果省略volatile,可能会陷入无限循环,因为第一个线程可能缓存stop的值。这里,volatile提示编译器在优化时要更加小心。