关于java:Volatile boolean vs AtomicBoolean

Volatile boolean vs AtomicBoolean

原子布尔能做什么,一个不稳定的布尔不能实现?


我使用易失性字段,当所述字段仅由其所有者线程更新并且值仅由其他线程读取时,您可以将其视为一个发布/订阅场景,其中有许多观察者,但只有一个发布者。但是,如果这些观察者必须根据字段的值执行一些逻辑,然后推回一个新的值,那么我将使用原子*变量、锁或同步块,无论什么最适合我。在许多并发场景中,它归结为获取值,将其与另一个值进行比较,并在必要时进行更新,因此原子*类中存在CompareAndSet和GetAndSet方法。

检查java.util.concurrent.atomic包的javadocs,以获取原子类的列表以及它们如何工作的极好解释(刚刚了解到它们是无锁的,因此它们比锁或同步块有优势)


它们完全不同。考虑一个volatile整数的例子:

1
2
3
4
volatile int i = 0;
void incIBy5() {
    i += 5;
}

如果两个线程同时调用函数,那么i之后可能是5,因为编译后的代码与此类似(除非不能在int上同步):

1
2
3
4
5
void incIBy5() {
    int temp;
    synchronized(i) { temp = i }
    synchronized(i) { i = temp + 5 }
}

如果一个变量是可变的,那么对它的每个原子访问都是同步的,但并不总是显而易见什么才是真正的原子访问。对于一个Atomic*对象,可以保证每个方法都是"原子的"。

因此,如果使用AtomicIntegergetAndAdd(int delta),您可以确定结果是10。同样地,如果两个线程同时对一个boolean变量求反,而对一个AtomicBoolean变量求反,则可以确定它在后面有原始值,而对一个volatile boolean变量则不能。

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

volatile的目的不同。考虑这个例子

1
2
3
4
5
volatile boolean stop = false;
void loop() {
    while (!stop) { ... }
}
void stop() { stop = true; }

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


不能将compareAndSetgetAndSet作为原子操作与volatile boolean(当然,除非同步它)。


AtomicBoolean具有自动执行复合操作的方法,而不必使用synchronized块。另一方面,如果在synchronized块内执行复合操作,则volatile boolean只能执行复合操作。

volatile boolean的读写记忆效应分别与AtomicBooleangetset方法相同。

例如,compareAndSet方法将自动执行以下操作(不带synchronized块):

1
2
3
4
5
6
if (value == expectedValue) {
    value = newValue;
    return true;
} else {
    return false;
}

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

1
2
3
4
5
6
7
final AtomicBoolean isJobDone = new AtomicBoolean(false);

...

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

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


volatile关键字保证发生在共享该变量的线程之间的关系之前。它不能保证两个或多个线程在访问该布尔变量时不会互相中断。


Volatile boolean vs AtomicBoolean

*《原子类包A型(波动的原始相同。从源。

1
2
3
4
5
6
7
8
9
10
11
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;
   }

所以如果你做的是把所有的原子和A *设置然后你不妨做一场好的波动。

What does AtomicBoolean do that a volatile boolean cannot achieve?

*给你这类原子方法提供更多先进的功能,如incrementAndGet()compareAndSet(),和他人,实现多业务(增量/获得/设置/测试集)没有锁定。这就是为什么这是如此强大的类原子的*。

例如,如果使用的是多线程使用下面的代码++,会有竞争条件,实际上是因为++:get,增量和集。

1
2
3
4
private volatile value;
...
// race conditions here
value++;

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

1
2
3
private final AtomicLong value = new AtomicLong();
...
value.incrementAndGet();

它是重要的注意,使用的原子波动场*缠绕类是一个好的方式来encapsulate共享关键资源从一个对象采取的。这意味着,开发商不能及时处理场如果不是共享问题是一个8场+;或其他代码,引入竞争条件。


如果有多个线程访问类级变量,那么每个线程都可以将该变量的副本保存在其线程本地缓存中。

使变量变为易失性将阻止线程在线程本地缓存中保留变量的副本。

原子变量是不同的,它们允许原子修改它们的值。


布尔型是原始原子的读操作和写在前,发生动荡的商业原则。所以如果你需要一个简单的get()和()的集合,然后你不需要的atomicboolean。

在其他的手,如果你需要一些设置的值之前,实施检查一变,例如"如果TRUE然后设置为false,那么你需要做这个手术atomically为好,本案例中使用的atomicboolean compareandset和其他方法提供的,因为如果你尝试实现这一动荡的布尔逻辑。我需要一些同步问题一些有价值的确信没有改变之间的get和set。


记住这个成语-

读-修改-写这个你不能用volatile实现的


如果你有一个线程的布尔只读修改,你可以使用A挥发性布尔(to define a,你本不stop变量在线程的主回路)。

然而,如果你有一个线程的多元性,在AtomicBoolean布尔,你应该使用。人是不安全的,下面的代码:

1
boolean r = !myVolatileBoolean;

这是在两个步骤:做手术

  • 一个布尔值读取。
  • 写的是布尔值。
  • 如果在其他线程之间的数据值和2##1,你可能得到一个错误的结果。AtomicBoolean避免这个问题的方法和步骤#1#2atomically做中学。