什么时候最好在 Java 中使用 volatile boolean 而不是 AtomicBoolean? [复制]
Posted
技术标签:
【中文标题】什么时候最好在 Java 中使用 volatile boolean 而不是 AtomicBoolean? [复制]【英文标题】:When is it preferable to use volatile boolean in Java rather than AtomicBoolean? [duplicate] 【发布时间】:2011-06-20 01:17:10 【问题描述】:我查看了 SO 中的其他 volatile 与 Atomicxxxx 问题(包括 this one)并阅读了 the description of java.util.current.atomic,但我对其中的细微差别不太满意。
如果我试图在使用volatile boolean
和AtomicBoolean
之间做出决定,除了AtomicBoolean 提供的原子读-修改-写操作之外,是否还有实际差异? (例如compareAndSet()
和getAndSet()
)
假设我有
volatile boolean flag;
然后一个或多个线程设置标志(但不清除它)。如果我有一个线程读取标志,并且如果设置,则执行一项操作,然后清除标志,volatile
是否足够?
就
而言,AtomicBoolean 是否比 volatile boolean 成本更高 内存空间 性能下降(volatile boolean
似乎需要内存防护,AtomicBoolean
似乎需要内存防护 + 根据 java.util.current.atomic 描述对 CAS 操作进行一些小锁定)
我的直觉是只使用 AtomicBoolean 并确保安全,但我想了解是否有任何情况可以使用 volatile boolean
代替(例如,如果我有数千个实例并且性能是一个问题)。
【问题讨论】:
你想用这个标志做什么?您是使用标志来控制并发还是其他?我想知道潜在的问题是否有助于解释您正在寻找的答案。If I have one thread that reads the flag, and if set, does an action, and then clears the flag, is volatile adequate?
是的,这正是 volatile
关键字应该为您解决的问题。
@Jonathan:这只是一个特定的例子......我正在考虑一个程序,其中我的组件标记为“脏”,需要持久性。组件将自己标记为“脏”,持久性管理器会找到脏组件,保存它们的状态,并将它们标记为非脏。
@BuZZ-dEE,这个问题早于我的问题,但我在撰写本文时已经阅读了它(在尝试使用 modpowers 之前,您能否完整阅读这些问题?)并且还有更多我提出的详细问题。所以是的,有重叠,但不,它不是重复的。
相关When I need to use AtomicBoolean in Java
【参考方案1】:
基本上所有AtomicBoolean
都是对象中的volatile boolean
。
每个对象都会有少量开销。可能微不足道,但可能需要更多内存进入缓存。
如果你需要使用AtomicBooleanFieldUpdater
,那么会有相当多的性能开销。如果你不打算经常这样做也可以(就像 NIO 中的attach
)。
【讨论】:
其实是volatile int
docjar.com/html/api/java/util/concurrent/atomic/…
@Helper 方法我认为这并不重要。完全取决于实现它的作用。
在内部,java 只喜欢整数,而不是任何其他原始类型。【参考方案2】:
然后一个或多个线程设置标志 (但不清楚)。如果我有一个 读取标志的线程,如果 设置,执行操作,然后清除 flag,volatile 够用吗?
是的,如果您不需要 AtomicBoolean 的特殊能力,那么使用 volatile 就可以了。事实上,这是 volatile 为数不多的合理用途之一。
【讨论】:
【参考方案3】:从实际角度来看,AtomicBoolean
和 volatile
之间的主要区别在于,比较和设置操作对于 volatile
变量不是原子操作。
volatile boolean b;
void foo()
if( b )
//Here another thread might have already changed the value of b to false
b = false;
但是看到你所有的并发写入都是幂等的并且你只从一个线程中读取,这应该不是问题。
【讨论】:
+1 用于提出幂等概念volatile
s 的语言中没有比较和设置操作,所以它不能是非原子的。库提供的比较和设置操作(AtomicBooleanFieldUpdate
和朋友)是原子的。
@Tom Hawtin - tackline 比较和设置(或交换)通常是一种操作,即使在 Java 中除了 Atomic*
类之外没有内置实现。 en.wikipedia.org/wiki/Compare-and-set
在您的示例中,无论发生什么,结果都是错误的。一个更好的例子是b = !b
。两个线程翻转b
它应该保持不变,但没有原子性,它可能会失败。
@maaartinus 我实际上试图证明的是,在 OP 描述的特定情况下,它并不重要,因为操作是幂等的。但正如你所说,b=!b
不是幂等的,因此它会失败。【参考方案4】:
我不确定我是否完全同意这里的其他答案; biziclop 的回答就目前而言是正确的,但我不确定我们能否得出结论,除非我们知道更多细节,否则您是安全的。
在简单的情况下,交错可能如下所示:
Thread 1 (writer) Thread 2 (Writer) Thread 3 (Reader)
----------------- ----------------- -----------------
flag = true;
if (flag)
flag = true;
flag = false;
doStuff();
这可能没问题(第二组flag
到true
无关紧要,因为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 需要设置其他状态,那么顺序也很重要。
在简单的情况下,是的,你很好,但如果它变得比简单的轻弹标志更复杂,那么这将变得更难推理。
【讨论】:
好点。尽管值得一提的是,在第二种情况下 AtomicBoolean 也无济于事。然后需要完全同步。 @Cowen:我从来没有见过这样可视化的交错,但很清楚。我在阅读线程代码时会在心里这样做,但这是在解释线程代码中的竞争条件/弱点时记录案例的好方法。 @SergeyTachenov 线程 3 中的 AtomicBoolean.getAndSet() 不会解决这个问题吗? @Андрей,我想它可以,但我们需要更多关于所有三个线程正在做什么的细节,以便准确地回答这个问题。它可以帮助线程 3 检测到线程 2 再次设置了标志,但无法帮助确定什么时候发生的,以及这些事情是否通过线程 2 的更新完成。【参考方案5】:这里有很多很好的信息。但是,我会添加另一个可能有用的区别。你可以有一个 AtomicBooleans 数组,但你不能(据我所知)有一个 volatile 布尔数组。
【讨论】:
不,但是您仍然可以拥有 SynchronizedSet 或其他同步的布尔值集,它们可以安全地发布数组以上是关于什么时候最好在 Java 中使用 volatile boolean 而不是 AtomicBoolean? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
Java进阶——volatile + JMM + 双重检查创建单例对象的时候为什么要加volatile关键字?!!
Java进阶—— volatile JMM双重检查以及创建单例对象的时候为什么要加volatile关键字?
volatile 关键字没用 啥时候用synchronized?