易失性布尔值与原子布尔值
AtomicBoolean 能做什么是 volatile 布尔值无法实现的?
What does AtomicBoolean do that a volatile boolean cannot achieve?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
AtomicBoolean 能做什么是 volatile 布尔值无法实现的?
What does AtomicBoolean do that a volatile boolean cannot achieve?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(12)
当所述字段仅由其所有者线程更新并且该值仅由其他线程读取时,我使用易失性字段,您可以将其视为发布/订阅场景,其中有许多观察者但只有一个发布者。然而,如果这些观察者必须根据字段的值执行一些逻辑,然后推回一个新值,那么我会使用 Atomic* vars 或锁或同步块,无论什么最适合我。在许多并发场景中,它归结为获取值,将其与另一个值进行比较,并在必要时进行更新,因此 Atomic* 类中存在compareAndSet 和 getAndSet 方法。
检查 的 JavaDocs java.util.concurrent.atomic 包,提供原子类列表以及它们如何工作的精彩解释(刚刚了解到它们是无锁的,因此它们比锁或同步块有优势)
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.
Check the JavaDocs of the java.util.concurrent.atomic package 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)
他们只是完全不同。考虑一下这个易失性整数的示例:
如果两个线程同时调用该函数,则 i 之后可能为 5,因为编译后的代码将与此有些类似(除非您不能同步
int
):如果变量是易失性的,则对它的每个原子访问都是同步的,但实际上什么是原子访问并不总是显而易见的。使用
Atomic*
对象,可以保证每个方法都是“原子的”。因此,如果您使用
AtomicInteger
和getAndAdd(int delta)
,则可以确定结果将为10
。同样,如果两个线程同时对一个boolean
变量取反,使用AtomicBoolean
您可以确保它之后具有原始值,使用易失性布尔值
,你不能。因此,每当您有多个线程修改某个字段时,您就需要使其原子化或使用显式同步。
易失性
的目的是不同的。考虑这个示例如果您有一个线程正在运行
loop()
,而另一个线程正在调用stop()
,那么如果您省略volatile
,您可能会遇到无限循环。 code>,因为第一个线程可能会缓存 stop 的值。在这里,易失性
充当编译器在优化时更加小心的提示。They are just totally different. Consider this example of a
volatile
integer: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 onint
):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".Thus, if you use an
AtomicInteger
andgetAndAdd(int delta)
, you can be sure that the result will be10
. In the same way, if two threads both negate aboolean
variable concurrently, with anAtomicBoolean
you can be sure it has the original value afterwards, with avolatile boolean
, you can't.So whenever you have more than one thread modifying a field, you need to make it atomic or use explicit synchronization.
The purpose of
volatile
is a different one. Consider this exampleIf you have a thread running
loop()
and another thread callingstop()
, you might run into an infinite loop if you omitvolatile
, since the first thread might cache the value of stop. Here, thevolatile
serves as a hint to the compiler to be a bit more careful with optimizations.您不能将
compareAndSet
、getAndSet
作为具有 易失性布尔值的原子操作(当然除非您同步它)。You can't do
compareAndSet
,getAndSet
as atomic operation with volatile boolean (unless of course you synchronize it).AtomicBoolean
具有以原子方式执行复合操作的方法,而无需使用synchronized
块。另一方面,如果在synchronized
块内执行复合操作,则 volatile boolean 只能执行复合操作。读/写 volatile boolean 的内存效果分别与 AtomicBoolean 的 get 和 set 方法相同。
例如,
compareAndSet
方法将自动执行以下操作(没有synchronized
块):因此,
compareAndSet
方法将允许您编写以下代码:即使从多个线程调用,也保证只执行一次。例如:保证只通知监听器一次(假设在将
AtomicBoolean
设置为true
后没有其他线程再次将其设置回false
)。AtomicBoolean
has methods that perform their compound operations atomically and without having to use asynchronized
block. On the other hand,volatile boolean
can only perform compound operations if done so within asynchronized
block.The memory effects of reading/writing to
volatile boolean
are identical to theget
andset
methods ofAtomicBoolean
respectively.For example the
compareAndSet
method will atomically perform the following (without asynchronized
block):Hence, the
compareAndSet
method will let you write code that is guaranteed to execute only once, even when called from multiple threads. For example:Is guaranteed to only notify the listener once (assuming no other thread sets the
AtomicBoolean
back tofalse
again after it being set totrue
).Atomic* 类包装了相同类型的 volatile 原语。从源头来看:
因此,如果您所做的只是获取和设置 Atomic* 那么您最好只拥有一个 volatile 字段。
Atomic* 类为您提供了提供更高级功能的方法,例如用于数字的
incrementAndGet()
、用于布尔值的compareAndSet()
以及实现多个操作(获取/增量)的其他方法。 /设置,测试/设置),无需锁定。这就是 Atomic* 类如此强大的原因。例如,如果多个线程使用
++
使用以下代码,则会出现竞争条件,因为++
实际上是:get、increment 和 set。但是,以下代码将在没有锁的情况下安全地在多线程环境中工作:
还需要注意的是,使用 Atomic* 类包装 易失性字段是从对象角度封装关键共享资源的好方法。这意味着开发人员不能只处理该字段,假设该字段不共享,可能会导致字段++出现注入问题;或其他引入竞争条件的代码。
The Atomic* classes wrap a volatile primitive of the same type. From the source:
So if all you are doing is getting and setting a Atomic* then you might as well just have a volatile field instead.
Atomic* classes give you methods that provide more advanced functionality such as
incrementAndGet()
for numbers,compareAndSet()
for booleans, and other methods that implement multiple operations (get/increment/set, test/set) without locking. That's why the Atomic* classes are so powerful.For example, if multiple threads are using the following code using
++
, there will be race conditions because++
is actually: get, increment, and set.However, the following code will work in a multi-threaded environment safely without locks:
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.
volatile
关键字保证共享该变量的线程之间发生先发生关系。它不能保证 2 个或更多线程在访问该布尔变量时不会互相中断。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.这里的很多答案都过于复杂、令人困惑,或者根本就是错误的。例如:
作为一般性陈述,这是不正确的。
这是不正确的;同步是完全不同的事情。
简单的答案是,
AtomicBoolean
允许您防止某些操作中的竞争条件,这些操作需要读取值,然后根据您读取的内容写入一个值;它使此类操作成为原子操作(即,它消除了变量在读取和写入之间可能发生变化的竞争条件)——因此得名。如果您只是读取和写入变量,其中写入不依赖于您刚刚读取的值,那么即使使用多个线程,
易失性
也可以正常工作。A lot of the answers here are overly complicated, confusing, or just wrong. For example:
That's incorrect as a general statement.
That is not correct; synchronization is a separate thing altogether.
The simple answer is that
AtomicBoolean
allows you to prevent race conditions in certain operations that require reading the value and then writing a value that depends on what you read; it makes such operations atomic (i.e. it removes the race condition where the variable might change between the read and the write)—hence the name.If you are just reading and writing the variable where the writes don't depend on a value you just read,
volatile
will work just fine, even with multiple threads.记住惯用语 -
读 - 修改 - 写,这是你无法用 volatile 实现的
Remember the IDIOM -
READ - MODIFY- WRITE this you can't achieve with volatile
如果有多个线程访问类级别变量那么
每个线程都可以在其线程本地缓存中保留该变量的副本。
使变量可变将阻止线程将变量的副本保留在线程本地缓存中。
原子变量是不同的,它们允许对其值进行原子修改。
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.
Atomic variables are different and they allow atomic modification of their values.
如果只有一个线程修改布尔值,则可以使用易失性布尔值(通常这样做是为了定义在线程的主循环中检查的
stop
变量)。但是,如果您有多个线程修改布尔值,则应使用
AtomicBoolean
。否则,下面的代码是不安全的:该操作分两步完成:
如果其他线程修改
#1
和2#
之间的值,您可能会得到错误的结果。AtomicBoolean
方法通过原子地执行步骤#1
和#2
来避免此问题。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).However, if you have multiple threads modifying the boolean, you should use an
AtomicBoolean
. Else, the following code is not safe:This operation is done in two steps:
If an other thread modify the value between
#1
and2#
, you might got a wrong result.AtomicBoolean
methods avoid this problem by doing steps#1
and#2
atomically.Boolean 原始类型对于写入和读取操作是原子的,易失性保证了happens-before 原则。因此,如果您需要简单的 get() 和 set(),那么您不需要 AtomicBoolean。
另一方面,如果您需要在设置变量的值之前进行一些检查,例如“如果为 true,则设置为 false”,那么您也需要以原子方式执行此操作,在这种情况下,请使用compareAndSet 和由AtomicBoolean,因为如果您尝试使用易失性布尔值实现此逻辑,您将需要一些同步以确保该值在 get 和 set 之间没有更改。
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.
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.
两者具有相同的概念,但在原子布尔中,它将为操作提供原子性,以防 cpu 切换发生在两者之间。
Both are of same concept but in atomic boolean it will provide atomicity to the operation in case the cpu switch happens in between.