什么时候在Java中使用volatile boolean比使用AtomicBoolean更可取?
我已经看过SO(包括)中的其他volatile vs.Atomicxxxx问题,并且已经阅读过,我对这些细微差别不太满意 如果我试图在使用什么时候在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; 然后一个或多个线程设置标志(但不清除)。如果我有一个线程读取标志,如果设置了,则执行一
volatile boolean
和AtomicBoolean
之间做出决定,那么除了AtomicBoolean提供的原子读修改写操作之外,还有实际的区别吗?(例如compareAndSet()
和getAndSet()
)
如果我有
volatile boolean flag;
然后一个或多个线程设置标志(但不清除)。如果我有一个线程读取标志,如果设置了,则执行一个操作,然后清除标志,volatile
是否足够
原子布尔的成本是否比volatile布尔的成本更高
- 存储空间
- 性能命中(
似乎需要内存隔离,volatile boolean
似乎需要内存隔离+根据java.util.current.atomic描述对CAS操作进行一些较小的锁定)AtomicBoolean
我的直觉是只使用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:我从来没有见过这样可视化的交错,但这是非常清楚的。我在阅读线程化代码时是在心里这样做的,但这是一种在解释竞争条件/弱点时记录案例的好方法。”