易失性布尔与AtomicBoolean

Jef*_*ffV 224 java concurrency boolean volatile atomicboolean

AtomicBoolean做什么,一个volatile布尔无法实现?

tet*_*eto 250

当所述字段仅由其所有者线程更新时,我使用volatile字段,并且该值仅由其他线程读取,您可以将其视为发布/订阅场景,其中有许多观察者但只有一个发布者.但是,如果这些观察者必须根据字段的值执行一些逻辑,然后推回一个新的值,那么我会使用Atomic*vars或锁或同步块,这些都适合我.在许多并发场景中,它归结为获取值,将其与另一个值进行比较并在必要时进行更新,因此在Atomic*类中存在compareAndSet和getAndSet方法.

检查java.util.concurrent.atomic包的JavaDocs以获取Atomic类的列表以及它们如何工作的优秀解释(只是了解到它们是无锁的,因此它们优于锁或同步块)

  • @ksl我认为@teto想要描述的是,如果只有一个线程修改“boolean”变量,我们应该选择“volatile boolean”。 (2认同)
  • 优秀的总结. (2认同)

Cep*_*pod 86

它们完全不同.考虑这个volatile整数的例子:

volatile int i = 0;
void incIBy5() {
    i += 5;
}
Run Code Online (Sandbox Code Playgroud)

如果两个线程同时调用该函数,i之后可能是5,因为编译后的代码将与此类似(除非您无法同步int):

void incIBy5() {
    int temp;
    synchronized(i) { temp = i }
    synchronized(i) { i = temp + 5 }
}
Run Code Online (Sandbox Code Playgroud)

如果变量是易失性的,则对它的每个原子访问都是同步的,但实际上有资格作为原子访问并不总是很明显.使用Atomic*对象,可以保证每个方法都是"原子的".

因此,如果您使用AtomicIntegergetAndAdd(int delta),您可以确定结果将是10.以同样的方式,如果两个线程同时否定一个boolean变量,AtomicBoolean你可以确定它之后具有原始值,用a volatile boolean,你不能.

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

目的volatile是不同的.考虑这个例子

volatile boolean stop = false;
void loop() {
    while (!stop) { ... }
}
void stop() { stop = true; }
Run Code Online (Sandbox Code Playgroud)

如果你有一个线程正在运行loop()而另一个线程正在调用stop(),那么如果省略则可能会遇到无限循环volatile,因为第一个线程可能会缓存stop的值.这里,volatile作为编译器的提示,使用优化更加小心.

  • -1:你给出了例子但没有真正解释volatile和Atomicxxxx之间的区别. (78认同)
  • 问题不在于"挥发性".问题是关于`volatile boolean` vs`AtomicBoolean`. (63认同)
  • -1:特别询问布尔值的问题,与其他数据类型相比,这是一个独特的案例,应该直接解释. (25认同)
  • -1`volatile`与同步无关. (8认同)
  • @ sgp15它与Java 5中的同步有关. (7认同)
  • 如果布尔值被许多线程读取,但只由一个线程写入,那么`volatile boolean`就足够了.如果还有很多编写器,您可能需要`AtomicBoolean`. (5认同)
  • volatile boolean上的`flag =!flag`不是线程安全的.如果你只使用原子变量的*`get`和`set`,那么应该可以进行切换. (4认同)
  • sgp15,volatile访问是同步动作(http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.2),因此,它们定义了并发的顺序使它们等同于锁定在同一监视器上的事件. (2认同)
  • 正确的答案应该是它为您提供了一个比较和设置函数,而这对于可变布尔值是不可能的(没有显式同步)。这个答案不是很清楚。越简单越好。我会要求OP考虑编辑答案,因为这是公认的答案。 (2认同)

nan*_*nda 52

你不能这样做compareAndSet,getAndSet因为使用volatile布尔值进行原子操作(当然除非你同步它).

  • 这是事实,但对布尔值来说这不是一个非常罕见的要求吗? (6认同)
  • @Robin 考虑使用它来控制初始化方法的延迟调用。 (2认同)

Nam*_*San 42

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

读/写的记忆效应分别与方法和方法volatile boolean相同.getsetAtomicBoolean

例如,该compareAndSet方法将原子地执行以下操作(没有synchronized块):

if (value == expectedValue) {
    value = newValue;
    return true;
} else {
    return false;
}
Run Code Online (Sandbox Code Playgroud)

因此,该compareAndSet方法将允许您编写保证仅执行一次的代码,即使从多个线程调用也是如此.例如:

final AtomicBoolean isJobDone = new AtomicBoolean(false);

...

if (isJobDone.compareAndSet(false, true)) {
    listener.notifyJobDone();
}
Run Code Online (Sandbox Code Playgroud)

保证只通知监听器一次(假设没有其他线程在设置AtomicBooleanfalse再次将其设置为true).


dhb*_*lah 14

volatile关键字保证发生在共享该变量的线程之间的关系之前.它并不能保证在访问该布尔变量时,2个或更多线程不会互相中断.

  • 布尔(在原始类型中)访问在Java中是原子的.读取和分配.因此没有其他线程会"中断"布尔操作. (13认同)
  • @ maciej-bilas欢迎引用JVM规范. (3认同)
  • 抱歉,但这如何回答这个问题?“Atomic*”类包装了一个“易失性”字段。 (2认同)

Gar*_*son 9

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

\n
\n

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

\n
\n

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

\n
\n

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

\n
\n

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

\n

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

\n

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

\n


Mov*_*ast 5

记住成语 -

读取 - 修改 - 写入这是您无法使用 volatile 实现的

  • 短,脆,切中要害。`volatile` 仅适用于所有者线程能够更新字段值而其他线程只能读取的情况。 (3认同)
  • @ChakladerAsfakArefe 不!正如 MoveFast 所说,**唯一**你不能做的事情就是读取+修改+写入。因此,例如,在工作线程中使用一些“易失性布尔keepRunning = true;”,两个不相关的线程可以在工作线程上调用设置“keepRunning = false;”的取消方法,并且工作线程将正确地选择最新写入的值。*唯一*不起作用的是“keepRunning = !keepRunning;”的顺序,因为这是一个读取-修改-写入。 (2认同)

小智 5

如果有多个线程访问类级别变量,则每个线程可以在其threadlocal缓存中保留该变量的副本.

使变量volatile将阻止线程在threadlocal缓存中保留变量的副本.

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


小智 5

布尔基元类型对于写和读操作是原子的,易失性保证了先发生原则。所以如果你需要一个简单的 get() 和 set() 那么你就不需要 AtomicBoolean。

另一方面,如果您需要在设置变量值之前进行一些检查,例如“如果为真则设置为假”,那么您也需要以原子方式执行此操作,在这种情况下使用 compareAndSet 和其他方法提供AtomicBoolean,因为如果您尝试使用 volatile boolean 实现此逻辑,您将需要一些同步以确保值在 get 和 set 之间没有更改。


Thi*_* D. 5

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

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

boolean r = !myVolatileBoolean;
Run Code Online (Sandbox Code Playgroud)

此操作分两步完成:

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

如果其他线程修改了#1和之间的值2#,您可能会得到错误的结果。AtomicBoolean方法通过执行步骤#1#2原子来避免这个问题。

  • “如果你只有一个线程修改你的布尔值,你可以使用一个易失性布尔值。”然后如果你使用一个线程,为什么你需要易失性(?)..你应该删除第一段以使答案更好.. (2认同)

Gra*_*ray 5

挥发性布尔值与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;
   }
Run Code Online (Sandbox Code Playgroud)

因此,如果您要做的只是获取并设置Atomic *,那么您也可能只拥有一个volatile字段。

可变布尔无法实现的AtomicBoolean有什么作用?

原子*类为您提供了更高级的功能,如方法incrementAndGet()compareAndSet()以及其他无锁实现多个操作(GET /递增/集,测试/套)。这就是Atomic *类如此强大的原因。

例如,如果多个线程使用,则下面的代码++将存在竞争条件,因为++实际上是:get,increment和set。

private volatile value;
...
// race conditions here
value++;
Run Code Online (Sandbox Code Playgroud)

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

private final AtomicLong value = new AtomicLong();
...
value.incrementAndGet();
Run Code Online (Sandbox Code Playgroud)

同样重要的是要注意,使用Atomic *类包装volatile字段是从对象的角度封装关键共享资源的好方法。这意味着开发人员不能仅仅假设未共享该字段就处理该字段,这可能会给field ++注入问题。或其他引入竞争条件的代码。