什么时候最好在 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 booleanAtomicBoolean 之间做出决定,除了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 intdocjar.com/html/api/java/util/concurrent/atomic/… @Helper 方法我认为这并不重要。完全取决于实现它的作用。 在内部,java 只喜欢整数,而不是任何其他原始类型。【参考方案2】:

然后一个或多个线程设置标志 (但不清楚)。如果我有一个 读取标志的线程,如果 设置,执行操作,然后清除 flag,volatile 够用吗?

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

【讨论】:

【参考方案3】:

从实际角度来看,AtomicBooleanvolatile 之间的主要区别在于,比较和设置操作对于 volatile 变量不是原子操作。

 volatile boolean b;

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

但是看到你所有的并发写入都是幂等的并且你只从一个线程中读取,这应该不是问题。

【讨论】:

+1 用于提出幂等概念 volatiles 的语言中没有比较和设置操作,所以它不能是非原子的。库提供的比较和设置操作(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();

这可能没问题(第二组flagtrue 无关紧要,因为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?

《Java程序猿面试笔试宝典》之volatile有什么作用

多线程下的volatile关键字使用详解及Java先行发生原则

volatile用法