我啥时候需要在 Java 中使用 AtomicBoolean?

Posted

技术标签:

【中文标题】我啥时候需要在 Java 中使用 AtomicBoolean?【英文标题】:When do I need to use AtomicBoolean in Java?我什么时候需要在 Java 中使用 AtomicBoolean? 【发布时间】:2011-05-28 22:10:45 【问题描述】:

我如何使用 AtomicBoolean 以及该类的用途是什么?

【问题讨论】:

相关:volatile boolean vs AtomicBoolean:***.com/questions/3786825/… 【参考方案1】:

当多个线程需要检查和更改布尔值时。例如:

if (!initialized) 
   initialize();
   initialized = true;

这不是线程安全的。您可以使用AtomicBoolean 修复它:

if (atomicInitialized.compareAndSet(false, true)) 
    initialize();

【讨论】:

这看起来不像是一个真实的例子 - 当initialize() 尚未完成时,其他线程可以看到true。因此,它仅在其他线程不关心 initialize() 的完成时才有效。 @axtavt:如果initialized 仅用于确保只有一个线程将调用initialize() 方法,我认为这是一个完全有效的真实示例。显然initialized 为真并不意味着在这种情况下初始化肯定已经完成,所以也许在这里稍微不同的术语会更好。同样,这取决于它的用途。 initStarted 和 initCompleted 需要 2 个布尔值,然后第一个线程设置 initStarted 并调用 initialise(),其余的等待 initCompleted 为真。 @Bozho - 对 boolean 字段的读取和写入是原子的,对吗?现在,volatile 为我提供了布尔字段的最新值。所以,实际上,volatile boolean 不会与AtomicBoolean 相同吗?。 @TheLostMind 我来晚了,但你会错过像compareAndSet 这样的功能,如果没有某种同步,这些功能实际上是无法实现的【参考方案2】:

这是我做的笔记(来自Brian Goetz book),可能对你有帮助

AtomicXXX 类

提供非阻塞比较和交换实现

利用提供的支持 通过硬件(CMPXCHG 指令 在 Intel 上)当有很多线程时 运行您使用的代码 这些原子并发 API,他们 将比代码更好地扩展 它使用对象级别 监视器/同步。自从, Java的同步机制 让代码等待,当有很多 通过你的线程运行 关键部分,一个实质性的 花费的 CPU 时间量 管理同步 机制本身(等待、通知、 等等)。由于新 API 使用硬件 级别构造(原子变量) 并等待并锁定免费算法 实现线程安全等等 的 CPU 时间用于“做事” 而不是在管理 同步。

不仅提供更好的 吞吐量,但它们也提供 对活力的抵抗力更强 死锁等问题 优先级倒置。

【讨论】:

您能否详细说明对 AtomicXXX 的访问是非阻塞的?谢谢【参考方案3】:

您可以使用原子布尔值有两个主要原因。首先它是可变的,例如,您可以将其作为引用传递并更改与布尔值本身关联的值。

public final class MyThreadSafeClass

    private AtomicBoolean myBoolean = new AtomicBoolean(false);
    private SomeThreadSafeObject someObject = new SomeThreadSafeObject();

    public boolean doSomething()
         someObject.doSomeWork(myBoolean);
         return myBoolean.get(); //will return true
    

在 someObject 类中

public final class SomeThreadSafeObject
    public void doSomeWork(AtomicBoolean b)
        b.set(true);
    

更重要的是,它是线程安全的,并且可以向维护类的开发人员表明,这个变量应该被修改并从多个线程中读取。如果您不使用 AtomicBoolean,则必须通过将其声明为 volatile 或围绕字段的读取和写入同步来同步您正在使用的布尔变量。

【讨论】:

看在上帝的份上,这只是为了展示对象本身的可变性。我专门写了这个是为了演示。 此外,如果这就是发生的一切,那么是的,它将始终返回 true 这并不能证明它是否是线程安全的。我可以完成我的 sn-ps 代码以使该类非常线程安全,但这只会扼杀我的观点。 我认为只有 Volatile 是不够的。考虑这样一种情况,两个线程直接从主内存读取和写入相同的值,这些线程之间没有任何同步 - 可能会出现并发问题。 你说得对,原子集然后检查操作是不够的,尽管 OP 没有足够的上下文来做出这个假设。说 volatile 可能还不够,这当然取决于具体情况。【参考方案4】:

AtomicBoolean 类为您提供一个布尔值,您可以自动更新该值。当你有多个线程访问一个布尔变量时使用它。

java.util.concurrent.atomic package overview 为您提供了有关此包中的类的作用以及何时使用它们的良好高级描述。我还推荐 Brian Goetz 的书 Java Concurrency in Practice。

【讨论】:

【参考方案5】:

摘自package description

包 java.util.concurrent.atomic 描述:一个小类工具包,支持对单个变量进行无锁线程安全编程。[...]

这些方法的规范使实现能够使用现代处理器上可用的高效机器级原子指令。[...]

AtomicBoolean、AtomicInteger、AtomicLong 和 AtomicReference 类的实例均提供对相应类型的单个变量的访问和更新。[...]

atomics 的访问和更新的记忆效应一般遵循 volatiles 的规则:

get 具有读取 volatile 变量的记忆效应。 set 具有写入(分配)易失性变量的记忆效应。 weakCompareAndSet 以原子方式读取和有条件地写入变量,相对于对该变量的其他内存操作进行排序,但在其他方面充当普通的非易失性内存操作。 compareAndSet 和所有其他读取和更新操作(例如 getAndIncrement)具有读取和写入 volatile 变量的记忆效应。

【讨论】:

以上是关于我啥时候需要在 Java 中使用 AtomicBoolean?的主要内容,如果未能解决你的问题,请参考以下文章

我啥时候需要在 Gradle 依赖项中使用 Kapt?

我啥时候需要在 Heroku 中使用工作进程

我啥时候需要使用存储在数据库中的访问令牌?

我啥时候需要使用四元数?

我啥时候应该参加内部课程[关闭]

我啥时候应该使用一对一的关系?