易失性布尔值与原子布尔值

发布于 2024-09-24 22:28:11 字数 45 浏览 1 评论 0原文

AtomicBoolean 能做什么是 volatile 布尔值无法实现的?

What does AtomicBoolean do that a volatile boolean cannot achieve?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

画尸师 2024-10-01 22:28:11

当所述字段仅由其所有者线程更新并且该值仅由其他线程读取时,我使用易失性字段,您可以将其视为发布/订阅场景,其中有许多观察者但只有一个发布者。然而,如果这些观察者必须根据字段的值执行一些逻辑,然后推回一个新值,那么我会使用 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)

永不分离 2024-10-01 22:28:11

他们只是完全不同。考虑一下这个易失性整数的示例:

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* 对象,可以保证每个方法都是“原子的”。

因此,如果您使用 AtomicIntegergetAndAdd(int delta),则可以确定结果将为 10。同样,如果两个线程同时对一个 boolean 变量取反,使用 AtomicBoolean 您可以确保它之后具有原始值,使用 易失性布尔值,你不能。

因此,每当您有多个线程修改某个字段时,您就需要使其原子化或使用显式同步。

易失性 的目的是不同的。考虑这个示例

volatile boolean stop = false;
void loop() {
    while (!stop) { ... }
}
void stop() { stop = true; }

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

They are just totally different. Consider this example of a volatile integer:

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):

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".

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.

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 example

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.

墨落成白 2024-10-01 22:28:11

您不能将 compareAndSetgetAndSet 作为具有 易失性布尔值的原子操作(当然除非您同步它)。

You can't do compareAndSet, getAndSet as atomic operation with volatile boolean (unless of course you synchronize it).

时光与爱终年不遇 2024-10-01 22:28:11

AtomicBoolean 具有以原子方式执行复合操作的方法,而无需使用 synchronized 块。另一方面,如果在 synchronized 块内执行复合操作,则 volatile boolean 只能执行复合操作。

读/写 volatile boolean 的内存效果分别与 AtomicBoolean 的 get 和 set 方法相同。

例如,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 设置为 true 后没有其他线程再次将其设置回 false )。

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.

The memory effects of reading/writing to volatile boolean are identical to the get and set methods of AtomicBoolean respectively.

For example the compareAndSet method will atomically perform the following (without a synchronized block):

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:

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).

清醇 2024-10-01 22:28:11

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 能做什么是 volatile 布尔值无法实现的?

Atomic* 类为您提供了提供更高级功能的方法,例如用于数字的 incrementAndGet()、用于布尔值的 compareAndSet() 以及实现多个操作(获取/增量)的其他方法。 /设置,测试/设置),无需锁定。这就是 Atomic* 类如此强大的原因。

例如,如果多个线程使用 ++ 使用以下代码,则会出现竞争条件,因为 ++ 实际上是:get、increment 和 set。

private volatile value;
...
// race conditions here
value++;

但是,以下代码将在没有锁的情况下安全地在多线程环境中工作:

private final AtomicLong value = new AtomicLong();
...
value.incrementAndGet();

还需要注意的是,使用 Atomic* 类包装 易失性字段是从对象角度封装关键共享资源的好方法。这意味着开发人员不能只处理该字段,假设该字段不共享,可能会导致字段++出现注入问题;或其他引入竞争条件的代码。

Volatile boolean vs AtomicBoolean

The Atomic* classes wrap a volatile primitive of the same type. From the source:

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.

What does AtomicBoolean do that a volatile boolean cannot achieve?

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.

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.

谁的年少不轻狂 2024-10-01 22:28:11

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.

烦人精 2024-10-01 22:28:11

这里的很多答案都过于复杂、令人困惑,或者根本就是错误的。例如:

...如果您有多个线程修改布尔值,则应该使用 AtomicBoolean

作为一般性陈述,这是不正确的。

如果变量是易失性的,则对它的每个原子访问都是同步的......

这是不正确的;同步是完全不同的事情。

简单的答案是,AtomicBoolean 允许您防止某些操作中的竞争条件,这些操作需要读取值,然后根据您读取的内容写入一个值;它使此类操作成为原子操作(即,它消除了变量在读取和写入之间可能发生变化的竞争条件)——因此得名。

如果您只是读取和写入变量,其中写入不依赖于您刚刚读取的值,那么即使使用多个线程,易失性也可以正常工作。

A lot of the answers here are overly complicated, confusing, or just wrong. For example:

… if you have multiple threads modifying the boolean, you should use an AtomicBoolean.

That's incorrect as a general statement.

If a variable is volatile, every atomic access to it is synchronized …

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.

旧时浪漫 2024-10-01 22:28:11

记住惯用语 -

读 - 修改 - 写,这是你无法用 volatile 实现的

Remember the IDIOM -

READ - MODIFY- WRITE this you can't achieve with volatile

丿*梦醉红颜 2024-10-01 22:28:11

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

使变量可变将阻止线程将变量的副本保留在线程本地缓存中。

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

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.

倚栏听风 2024-10-01 22:28:11

如果只有一个线程修改布尔值,则可以使用易失性布尔值(通常这样做是为了定义在线程的主循环中检查的stop变量)。

但是,如果您有多个线程修改布尔值,则应使用 AtomicBoolean。否则,下面的代码是不安全的:

boolean r = !myVolatileBoolean;

该操作分两步完成:

  1. 读取布尔值。
  2. 布尔值被写入。

如果其他线程修改 #12# 之间的值,您可能会得到错误的结果。 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:

boolean r = !myVolatileBoolean;

This operation is done in two steps:

  1. The boolean value is read.
  2. 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.

菊凝晚露 2024-10-01 22:28:11

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.

-残月青衣踏尘吟 2024-10-01 22:28:11

两者具有相同的概念,但在原子布尔中,它将为操作提供原子性,以防 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文