什么时候在Java中使用volatile boolean比使用AtomicBoolean更可取?

什么时候在Java中使用volatile boolean比使用AtomicBoolean更可取?,java,concurrency,atomic,volatile,Java,Concurrency,Atomic,Volatile,我已经看过SO(包括)中的其他volatile vs.Atomicxxxx问题,并且已经阅读过,我对这些细微差别不太满意 如果我试图在使用volatile boolean和AtomicBoolean之间做出决定,那么除了AtomicBoolean提供的原子读修改写操作之外,还有实际的区别吗?(例如compareAndSet()和getAndSet()) 如果我有 volatile boolean flag; 然后一个或多个线程设置标志(但不清除)。如果我有一个线程读取标志,如果设置了,则执行一

我已经看过SO(包括)中的其他volatile vs.Atomicxxxx问题,并且已经阅读过,我对这些细微差别不太满意

如果我试图在使用
volatile boolean
AtomicBoolean
之间做出决定,那么除了AtomicBoolean提供的原子读修改写操作之外,还有实际的区别吗?(例如
compareAndSet()
getAndSet()

如果我有

volatile boolean flag;
然后一个或多个线程设置标志(但不清除)。如果我有一个线程读取标志,如果设置了,则执行一个操作,然后清除标志,
volatile
是否足够

原子布尔的成本是否比volatile布尔的成本更高

  • 存储空间
  • 性能命中(
    volatile boolean
    似乎需要内存隔离,
    AtomicBoolean
    似乎需要内存隔离+根据java.util.current.atomic描述对CAS操作进行一些较小的锁定)

我的直觉是只使用AtomicBoolean并确保安全,但我想了解是否有使用
volatile boolean
的情况(例如,如果我有数千个实例,性能是一个问题)。

基本上所有
AtomicBoolean
都是对象中的
volatile boolean

每个对象将有一个小的开销。可能不重要,但可能需要更多内存才能进入缓存

如果您需要使用AtomicBooleanFieldUpdater,那么会有相当大的性能开销。如果您不经常这样做(如NIO中的
attach

然后一个或多个线程设置该标志 (但不清楚)。如果我有 读取标志的线程,如果 设置,执行操作,然后清除 旗帜,是否足够


是的,如果你不需要原子布尔的特殊能力,那么使用volatile就可以了。事实上,这是volatile为数不多的合理用途之一。

从实用角度来看,
原子布尔
volatile
之间的主要区别在于,比较和设置操作不是带有
volatile
变量的原子操作

 volatile boolean b;

 void foo() {
   if( b ) {
     //Here another thread might have already changed the value of b to false
     b = false;
   }
 }

但是,由于您所有的并发写入都是幂等的,并且您只从一个线程读取,所以这应该不是一个问题。

我不确定我是否完全同意这里的其他答案;就目前而言,biziclop的回答是正确的,但除非我们知道更多细节,否则我不确定我们能得出你是安全的结论

在简单的情况下,交错可能如下所示:

Thread 1 (writer)   Thread 2 (Writer)  Thread 3 (Reader)
-----------------   -----------------  -----------------
flag = true;
                                       if (flag) {
                    flag = true;
                                         flag = false;
                                         doStuff();
这可能没问题(第二组
标志
并不重要,因为
doStuff()
可能仍然会看到线程2需要执行的任何操作

但是,如果颠倒顺序,线程3会:

Thread 1 (writer)   Thread 2 (Writer)  Thread 3 (Reader)
-----------------   -----------------  -----------------
flag = true;
                                       if (flag) {
                                         doStuff();
                    flag = true;
                                         flag = false;
那么线程2的更新可能会丢失

当然,对于线程2执行的任何其他操作,您都需要同样小心,以确保它对线程3可见。如果线程2需要设置其他状态,那么顺序在那里也变得很重要


在简单的情况下,是的,你很好,但是如果它变得比简单的轻弹旗帜更复杂,那么这就更难解释了。

这里有很多好的信息。但是,我想补充另一个可能有用的区别。你可以有一个原子布尔数组,但你不能(据我所知)有一个可变布尔值数组。

你想用这个标志做什么?你是用这个标志来控制并发还是其他什么?我想知道潜在的问题是否有助于解释你想要找到的答案。
如果我有一个线程读取标志,如果设置了,则执行一个操作,然后清除标志,那么volatile是否足够?
是的,这正是
volatile
关键字应该为您解决的问题。@Jonathan:这只是一个特殊的例子……我在想一个程序,其中我将组件标记为“脏”,需要持久化。组件将自己标记为“脏”,持久性管理器会找到脏组件,保存它们的状态,并将它们标记为非脏组件。@BuZZ dEE,这个问题早于我的问题,但我在写这篇文章时就已经读过了(在尝试使用ModPower之前,您能完整地阅读这些问题吗?)我提出了一些更详细的问题。因此,是的,有重叠,但不,它不是重复的。它实际上是一个
volatile int
@Helper方法,我不认为这有什么关系。完全取决于它的实现。在内部,java只喜欢整数,比任何其他基元类型都喜欢整数。+1用于提出幂等元ent CONCEPTION在
volatile
s的语言中没有比较和设置操作,因此它不能是独立的。库提供的比较和设置操作(
AtomicBooleanFieldUpdate
和friends)是原子的。@Tom Hawtin-tackline比较和设置(或交换)尽管如此,它仍然是一个操作,尽管在java中除了
原子*
类之外没有内置的实现。在您的示例中,无论发生什么,结果都是错误的。更好的示例是
b=!b
。当两个线程翻转
b
时,它应该保持不变,但如果没有原子性,它可以il.@maaartinus我实际上试图证明的是,在OP描述的特定情况下,它并不重要,因为运算是幂等的。但是正如你所说,
b=!b
不是幂等的,因此它将失败。很好的一点。尽管值得一提的是,在第二种情况下,原子布尔也不会有帮助。完全成熟的同步“那么就需要进行优化了。@Cowen:我从来没有见过这样可视化的交错,但这是非常清楚的。我在阅读线程化代码时是在心里这样做的,但这是一种在解释竞争条件/弱点时记录案例的好方法。”