Java 挥发性布尔值与原子布尔值
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3786825/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Volatile boolean vs AtomicBoolean
提问by JeffV
What does AtomicBoolean do that a volatile boolean cannot achieve?
AtomicBoolean 做了哪些 volatile boolean 无法实现的事情?
采纳答案by Cephalopod
They are just totally different. Consider this example of a volatile
integer:
他们只是完全不同。考虑这个volatile
整数的例子:
volatile int i = 0;
void incIBy5() {
i += 5;
}
If two threads call the function concurrently, i
might be 5 afterwards, since the compiled code will be somewhat similar to this (except you cannot synchronize on int
):
如果两个线程同时调用该函数,则i
之后可能是 5,因为编译后的代码将与此有些相似(除非您无法同步int
):
void incIBy5() {
int temp;
synchronized(i) { temp = i }
synchronized(i) { i = temp + 5 }
}
If a variable is volatile, every atomic access to it is synchronized, but it is not always obvious what actually qualifies as an atomic access. With an Atomic*
object, it is guaranteed that every method is "atomic".
如果变量是 volatile,则对它的每次原子访问都是同步的,但实际上什么是原子访问并不总是很明显。对于一个Atomic*
对象,可以保证每个方法都是“原子的”。
Thus, if you use an AtomicInteger
and getAndAdd(int delta)
, you can be sure that the result will be 10
. In the same way, if two threads both negate a boolean
variable concurrently, with an AtomicBoolean
you can be sure it has the original value afterwards, with a volatile boolean
, you can't.
因此,如果您使用AtomicInteger
and getAndAdd(int delta)
,您可以确定结果将是10
。以同样的方式,如果两个线程同时否定一个boolean
变量,使用 aAtomicBoolean
可以确定它之后具有原始值,使用 avolatile boolean
则不能。
So whenever you have more than one threadmodifying a field, you need to make it atomic or use explicit synchronization.
因此,每当您有多个线程修改一个字段时,您都需要使其原子化或使用显式同步。
The purpose of volatile
is a different one. Consider this example
的目的volatile
是不同的。考虑这个例子
volatile boolean stop = false;
void loop() {
while (!stop) { ... }
}
void stop() { stop = true; }
If you have a thread running loop()
and another thread calling stop()
, you might run into an infinite loop if you omit volatile
, since the first thread might cache the value of stop. Here, the volatile
serves as a hint to the compiler to be a bit more careful with optimizations.
如果您有一个线程正在运行loop()
而另一个线程正在调用stop()
,则如果省略volatile
,您可能会遇到无限循环,因为第一个线程可能会缓存 stop 的值。在这里,volatile
提示编译器在优化时要更加小心。
回答by nanda
You can't do compareAndSet
, getAndSet
as atomic operation with volatile boolean (unless of course you synchronize it).
你不能做compareAndSet
,getAndSet
作为 volatile boolean 的原子操作(除非你同步它)。
回答by dhblah
volatile
keyword guarantees happens-before relationship among threads sharing that variable. It doesn't guarantee you that 2 or more threads won't interrupt each other while accessing that boolean variable.
volatile
关键字保证共享该变量的线程之间的发生在关系之前。它不能保证 2 个或更多线程在访问该布尔变量时不会相互中断。
回答by teto
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.
当所述字段仅由其所有者线程更新并且该值仅由其他线程读取时,我使用 volatile 字段,您可以将其视为一个发布/订阅场景,其中有许多观察者但只有一个发布者。但是,如果这些观察者必须根据字段的值执行一些逻辑,然后推回一个新值,那么我会使用 Atomic* 变量或锁或同步块,任何最适合我的方法。在许多并发场景中,它归结为获取值,将其与另一个值进行比较并在必要时进行更新,因此存在于 Atomic* 类中的 compareAndSet 和 getAndSet 方法。
Check the JavaDocs of the java.util.concurrent.atomicpackage for a list of Atomic classes and an excellent explanation of how they work (just learned that they are lock-free, so they have an advantage over locks or synchronized blocks)
检查java.util.concurrent.atomic包的 JavaDocs 以获取 Atomic 类列表以及它们如何工作的出色解释(刚刚了解到它们是无锁的,因此它们比锁或同步块有优势)
回答by MoveFast
Remember the IDIOM -
记住成语 -
READ - MODIFY- WRITE this you can't achieve with volatile
读取 - 修改 - 写入这是您无法使用 volatile 实现的
回答by Amol Gaikwad
If there are multiple threads accessing class level variable then each thread can keep copy of that variable in its threadlocal cache.
如果有多个线程访问类级别变量,则每个线程都可以在其线程本地缓存中保留该变量的副本。
Making the variable volatile will prevent threads from keeping the copy of variable in threadlocal cache.
使变量 volatile 将阻止线程在线程本地缓存中保留变量的副本。
Atomic variables are different and they allow atomic modification of their values.
原子变量是不同的,它们允许对其值进行原子修改。
回答by Nam San
AtomicBoolean
has methods that perform their compound operations atomically and without having to use a synchronized
block. On the other hand, volatile boolean
can only perform compound operations if done so within a synchronized
block.
AtomicBoolean
具有原子地执行其复合操作的方法,而无需使用synchronized
块。另一方面,volatile boolean
如果在synchronized
块内执行复合操作,则只能执行复合操作。
The memory effects of reading/writing to volatile boolean
are identical to the get
and set
methods of AtomicBoolean
respectively.
读/写的记忆效应volatile boolean
分别与get
和set
方法相同AtomicBoolean
。
For example the compareAndSet
method will atomically perform the following (without a synchronized
block):
例如,该compareAndSet
方法将自动执行以下操作(没有synchronized
块):
if (value == expectedValue) {
value = newValue;
return true;
} else {
return false;
}
Hence, the compareAndSet
method will let you write code that is guaranteed to execute only once, even when called from multiple threads. For example:
因此,该compareAndSet
方法将让您编写保证仅执行一次的代码,即使从多个线程调用也是如此。例如:
final AtomicBoolean isJobDone = new AtomicBoolean(false);
...
if (isJobDone.compareAndSet(false, true)) {
listener.notifyJobDone();
}
Is guaranteed to only notify the listener once (assuming no other thread sets the AtomicBoolean
back to false
again after it being set to true
).
保证只通知侦听器一次(假设没有其他线程在设置为AtomicBoolean
后false
再次将返回设置为true
)。
回答by user2660000
Boolean primitive type is atomic for write and read operations, volatile guarantees the happens-before principle. So if you need a simple get() and set() then you don't need the AtomicBoolean.
布尔基元类型对于写和读操作是原子的,易失性保证了先发生原则。所以如果你需要一个简单的 get() 和 set() 那么你就不需要 AtomicBoolean。
On the other hand if you need to implement some check before setting the value of a variable, e.g. "if true then set to false", then you need to do this operation atomically as well, in this case use compareAndSet and other methods provided by AtomicBoolean, since if you try to implement this logic with volatile boolean you'll need some synchronization to be sure that the value has not changed between get and set.
另一方面,如果您需要在设置变量值之前进行一些检查,例如“如果为真则设置为假”,那么您也需要以原子方式执行此操作,在这种情况下,请使用 compareAndSet 和其他提供的方法AtomicBoolean,因为如果您尝试使用 volatile boolean 实现此逻辑,您将需要一些同步以确保值在 get 和 set 之间没有更改。
回答by Thibaut D.
If you have only one thread modifying your boolean, you can use a volatile boolean(usually you do this to define a stop
variable checked in the thread's main loop).
如果只有一个线程在修改布尔值,则可以使用 volatile 布尔值(通常这样做是为了定义stop
在线程的主循环中检查的变量)。
However, if you have multiple threads modifying the boolean, you should use an AtomicBoolean
. Else, the following code is not safe:
但是,如果您有多个线程修改布尔值,则应使用AtomicBoolean
. 否则,以下代码是不安全的:
boolean r = !myVolatileBoolean;
This operation is done in two steps:
此操作分两步完成:
- The boolean value is read.
- The boolean value is written.
- 读取布尔值。
- 写入布尔值。
If an other thread modify the value between #1
and 2#
, you might got a wrong result. AtomicBoolean
methods avoid this problem by doing steps #1
and #2
atomically.
如果其他线程修改了#1
和之间的值2#
,您可能会得到错误的结果。AtomicBoolean
方法通过执行步骤#1
和#2
原子来避免这个问题。
回答by Gray
Volatile boolean vs AtomicBoolean
挥发性布尔值与原子布尔值
The Atomic* classes wrap a volatile primitive of the same type. From the source:
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;
}
So if all you are doing is getting and setting a Atomic* then you might as well just have a volatile field instead.
因此,如果您所做的只是获取和设置 Atomic*,那么您最好只拥有一个 volatile 字段。
What does AtomicBoolean do that a volatile boolean cannot achieve?
AtomicBoolean 做了哪些 volatile boolean 无法实现的事情?
Atomic* classes give you methods that provide more advanced functionality such as incrementAndGet()
, compareAndSet()
, and others that implement multiple operations (get/increment/set, test/set) without locking. That's why the Atomic* classes are so powerful.
原子*类为您提供了更高级的功能,如方法incrementAndGet()
,compareAndSet()
以及其他无锁实现多个操作(GET /递增/集,测试/套)。这就是 Atomic* 类如此强大的原因。
For example, if multiple threads are using the following code using ++
, there will be race conditions because ++
is actually: get, increment, and set.
例如,如果多个线程使用以下代码 using ++
,则会出现竞争条件,因为++
实际上是:get、increment 和 set。
private volatile value;
...
// race conditions here
value++;
However, the following code will work in a multi-threaded environment safely without locks:
但是,以下代码将在没有锁的情况下安全地在多线程环境中工作:
private final AtomicLong value = new AtomicLong();
...
value.incrementAndGet();
It's also important to note that wrapping your volatile field using Atomic* class is a good way to encapsulate the critical shared resource from an object standpoint. This means that developers can't just deal with the field assuming it is not shared possibly injecting problems with a field++; or other code that introducing race conditions.
同样重要的是要注意,使用 Atomic* 类包装 volatile 字段是从对象的角度封装关键共享资源的好方法。这意味着开发人员不能假设它没有被共享可能会注入字段 ++ 的问题。或其他引入竞争条件的代码。