什么时候需要在Java中使用AtomicBoolean?


Answers:


244

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

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

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

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

51
它看起来不像是一个真实的示例-其他线程可以看到true何时initialize()尚未完成。因此,仅当其他线程不关心的完成时它才起作用initialize()
axtavt 2010年

6
@axtavt:如果initialized仅用于确保只有一个线程将调用该initialize()方法,那么我认为这是一个完全有效的真实示例。显然initialized是真实的,并不意味着初始化在这种情况下,肯定完成,所以也许稍有不同的任期将是更好地在这里。同样,这取决于它的用途。
ColinD 2010年

14
您需要为initStarted和initCompleted使用2个布尔值,然后第一个线程设置initStarted并调用initialise(),其余线程等待initCompleted为true。
马丁2010年

3
@Bozho-对布尔字段的读写是原子对吗?,现在,volatile给了我布尔字段的最新值。因此,有效地,将volatile booleanAtomicBoolean?不相同。
TheLostMind 2014年

2
@Martin:没有直接的方法来等待布尔值变为真;您需要其他机制。最明智的方法是使用一个synchronized块,在这种情况下,您不再需要AtomicBoolean,而只需一个volatile boolean。(if(! this.initialized) { synchronized(this) { if(! this.initialized) { initialize(); this.initialized = true; } } }将确保只有一个线程被调用initialize,并且确保所有其他线程都已等待它进行调用,前提是initialized已标记volatile。)
ruakh

52

这是我做的笔记(摘自Brian Goetz的书),可能对您有帮助

AtomicXXX类

  • 提供无阻塞的比较和交换实现

  • 充分利用硬件提供的支持(Intel上的CMPXCHG指令)当大量线程正在使用这些原子并发API的代码中运行时,它们的伸缩性将比使用对象级监视器/同步的代码好得多。由于Java的同步机制使代码处于等待状态,因此当关键部分中运行大量线程时,将花费大量CPU时间来管理同步机制本身(等待,通知等)。由于新的API使用硬件级别的构造(原子变量)以及无需等待和锁定的算法来实现线程安全,因此“做某事”而不是在管理同步上花费了大量的CPU时间。

  • 不仅可以提供更好的吞吐量,而且还可以更好地抵御死锁和优先级反转等活动问题。


34

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

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,则必须通过声明其易失性或在字段的读写周围进行同步来同步所使用的布尔变量。


4
对于上帝的爱,那仅仅是为了表明物体本身的可变性。我专门为演示目的而写。
约翰·温特2010年

再者,如果这是所有发生的事情,那么是的,它将始终返回true
John Vint 2010年

事实证明这不是线程安全的。我可以完成代码片段以使该类非常线程安全,但这只会扼杀我的观点。
约翰·维特

1
我认为仅挥发物是不够的。考虑一下这样一种情况,其中两个线程直接从主内存中读取和写入相同的值,这些线程之间没有任何同步-可能会出现并发问题。
Shay Tsadok 2015年

1
您是对的,尽管OP中没有足够的上下文来进行此假设,但对于原子集然后检查操作是不够的。可以说,根据情况的不同,挥发可能永远不够。
John Vint


5

包装说明摘录

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

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

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

原子访问和更新的记忆效应通常遵循挥发物的规则:

  • get具有读取易失性变量的存储效果。
  • set具有写入(分配)volatile变量的存储效果。
  • weakCompareAndSet以原子方式读取和有条件地写入变量,相对于该变量上的其他内存操作而言是有序的,否则将用作普通的非易失性内存操作。
  • compareAndSet和所有其他读取和更新操作(如getAndIncrement)具有读取和写入易失性变量的内存影响。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.