3 回答
TA贡献1946条经验 获得超3个赞
它们完全不同。考虑这个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和getAndAdd(int delta),您可以确定结果将是10。以同样的方式,如果两个线程同时否定一个boolean变量,AtomicBoolean你可以确定它之后具有原始值,用a volatile boolean,你不能。
因此,每当您有多个线程修改字段时,您需要使其成为原子或使用显式同步。
目的volatile是不同的。考虑这个例子
volatile boolean stop = false;
void loop() {
while (!stop) { ... }
}
void stop() { stop = true; }
如果你有一个线程正在运行loop()而另一个线程正在调用stop(),那么如果省略则可能会遇到无限循环volatile,因为第一个线程可能会缓存stop的值。这里,volatile作为编译器的提示,使用优化更加小心。
TA贡献1830条经验 获得超3个赞
当所述字段仅由其所有者线程更新时,我使用volatile字段,并且该值仅由其他线程读取,您可以将其视为发布/订阅场景,其中有许多观察者但只有一个发布者。但是,如果这些观察者必须根据字段的值执行一些逻辑,然后推回一个新的值,那么我会使用Atomic * vars或锁或同步块,这些都适合我。在许多并发场景中,它归结为获取值,将其与另一个值进行比较并在必要时进行更新,因此在Atomic *类中存在compareAndSet和getAndSet方法。
检查java.util.concurrent.atomic包的JavaDocs以获取Atomic类的列表以及它们如何工作的优秀解释(只是了解到它们是无锁的,因此它们优于锁或同步块)
TA贡献1815条经验 获得超10个赞
AtomicBoolean具有以原子方式执行其复合操作而无需使用synchronized块的方法。另一方面,volatile boolean只能在synchronized块内执行复合操作。
读/写的记忆效应分别与方法和方法volatile boolean相同。getsetAtomicBoolean
例如,该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后false再次将其设置为true)。
添加回答
举报