可变布尔无法实现的AtomicBoolean有什么作用?
可变布尔无法实现的AtomicBoolean有什么作用?
Answers:
它们完全不同。考虑以下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 }
}
如果变量是易失性的,则对它的每个原子访问都是同步的,但实际上什么才算是原子访问并不总是很明显。对于一个Atomic*
对象,可以保证每种方法都是“原子的”。
因此,如果使用AtomicInteger
and getAndAdd(int delta)
,则可以确保结果为10
。以同样的方式,如果两个线程同时对一个boolean
变量AtomicBoolean
求反,则使用a可以确保它之后具有原始值,而使用a volatile boolean
则不能。
因此,只要您有多个线程修改一个字段,就需要使其成为原子的或使用显式同步。
的目的volatile
是不同的。考虑这个例子
volatile boolean stop = false;
void loop() {
while (!stop) { ... }
}
void stop() { stop = true; }
如果您正在运行一个线程loop()
而另一个线程正在调用stop()
,则如果您忽略它volatile
,则可能会陷入无限循环,因为第一个线程可能会缓存stop的值。在这里,这volatile
暗示了编译器在优化时会更加谨慎。
volatile
。现在的问题是关于volatile boolean
VS AtomicBoolean
。
volatile boolean
足够。如果作家很多,那么您可能需要AtomicBoolean
。
当该字段仅由其所有者线程更新并且该值仅由其他线程读取时,我使用易失性字段,您可以将其视为发布/订阅方案,其中有许多观察者,但只有一个发布者。但是,如果那些观察者必须根据字段的值执行一些逻辑,然后推回新的值,那么我会选择最适合我的Atomic * var,锁或同步块。在许多并发场景中,它归结为获取值,将其与另一个值进行比较,并在必要时进行更新,因此Atomic *类中存在compareAndSet和getAndSet方法。
检查java.util.concurrent.atomic包的JavaDocs,以获取原子类的列表以及它们如何工作的极好说明(刚刚了解到它们是无锁的,因此它们比锁或同步块有优势)
boolean
var,则应该选择volatile boolean
。
你不能这样做compareAndSet
,getAndSet
因为挥发性布尔原子操作(当然,除非你进行同步)。
AtomicBoolean
有一些方法可以自动执行其复合操作,而不必使用synchronized
块。另一方面,volatile boolean
仅在synchronized
块内执行复合操作才可以执行。
读/写to的记忆效应volatile boolean
分别与get
和set
的AtomicBoolean
相同。
例如,该compareAndSet
方法将自动执行以下操作(无synchronized
块):
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
back )。false
true
volatile
关键字保证在共享该变量的线程之间发生事前关系。它不能保证您在访问该布尔变量时2个或更多线程不会互相中断。
Atomic*
类包装一个volatile
字段。
挥发性布尔值与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有什么作用?
原子*类为您提供了更高级的功能,如方法incrementAndGet()
,compareAndSet()
以及其他无锁实现多个操作(GET /递增/集,测试/套)。这就是Atomic *类如此强大的原因。
例如,如果多个线程使用,则下面的代码++
将存在竞争条件,因为++
实际上是:get,increment和set。
private volatile value;
...
// race conditions here
value++;
但是,以下代码将在没有锁的情况下安全地在多线程环境中运行:
private final AtomicLong value = new AtomicLong();
...
value.incrementAndGet();
同样重要的是要注意,使用Atomic *类包装volatile字段是从对象的角度封装关键共享资源的好方法。这意味着开发人员不能仅仅假设未共享该字段就处理该字段,这可能会给field ++注入问题。或其他引入竞争条件的代码。
如果有多个线程访问类级别的变量,则每个线程都可以将该变量的副本保留在其线程本地缓存中。
将变量设为volatile将防止线程将变量的副本保留在threadlocal缓存中。
原子变量不同,它们允许对其值进行原子修改。
记住IDIOM-
读-修改-写这是使用volatile无法实现的
volatile
仅在所有者线程具有更新字段值的能力而其他线程只能读取的情况下才起作用。