为什么AtomicBoolean不能替代布尔值?

时间:2016-04-29 14:55:25

标签: java api-design

AtomicBoolean的Oracle JDK Javadoc声明:

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicBoolean.html

  

可以原子方式更新的布尔值。见   java.util.concurrent.atomic包规范的描述   原子变量的属性。使用AtomicBoolean   应用程序如原子更新的标志,不能用作   替换布尔值。

一位同事和我试图找出一个用例,其中AtomicBoolean不能替代,我们唯一能想到的是布尔对象有的方法,而AtomicBoolean却没有。

这是唯一的原因还是在撰写时还有其他想法?

6 个答案:

答案 0 :(得分:53)

Boolean是一个不可变的值对象。它被设计为不变的并且是最终的,以便执行它。 java.lang.Boolean从1.0开始就存在。

AtomicBoolean是可变的,旨在进行更新,以便跨线程可以看到更新后的值。 AtomicBoolean是在Java 5中引入的。

这些是完全不同的概念,这就是为什么AtomicBoolean不是为扩展布尔而设计的。你不能用可变对象替换不可变对象而不用使用它来破坏代码的预期不变量。如果原子版本可以在其位置传入,则期望接收不可变值的代码可能会被破坏。

所以这里是一个用例:如果AtomicBoolean是作为可替代布尔值的东西引入的,你可以有一种情况,在这个改变之前创建的一个类可以合理地期望在某些方法中返回一个布尔它没有&# 39;由于布尔值是不可变的,因此需要传递防御性副本。如果返回的引用恰好从更改为使用AtomicBoolean而不是Boolean的源进行初始化,那么现在可以通过调用返回布尔值的方法修改该字段,方法是将其转换为AtomicBoolean。

原子类设计用于处理并发更新(作为volatile的改进),但设计并发代码的最有效方法是使用不可变值。因此,请注意不要将AtomicBoolean误认为"编写多线程代码时使用的布尔值"。

答案 1 :(得分:33)

Boolean是原始boolean周围的包装类。它可以由编译器(装箱转换)从boolean自动创建,也可以转换为布尔值(拆箱转换)。 AtomicBoolean不是这种情况,它是为并发目的而设计的单独类。

因此,这两个类在语言层面具有不同的语义:

Boolean b = new Boolean(true);
AtomicBoolean ab = new AtomicBoolean(true);
System.out.println(true == b);  // automatic unboxing of Boolean variable
System.out.println(true == ab);  // compiler error

答案 2 :(得分:4)

它们不能自动装箱,所以它们不能用于条件,例如,

// Explodey
if (someAtomicBoolean) {
}

答案 3 :(得分:4)

示例:

void doSomething( final Boolean flag ) {


  final boolean before = flag.booleanValue();

  do0( flag );

  final boolean after = flag.booleanValue();

  assert before == after;



  if ( flag.booleanValue() ) {
    do1();
  }

  if ( flag.booleanValue() ) {
    do2();
  }

}

可以提供与

不同的结果
void doSomething( final AtomicBoolean flag ) {


  final boolean before = flag.get();

  do0( flag );

  final boolean after = flag.get();

  assert (before == after) || (before != after);



  if ( flag.get() ) {
    do1();
  }

  if ( flag.get() ) {
    do2();
  }

}

因为AtomicBoolean可以更改其值,而Boolean则不能。

在第一种情况下,do1()do2()要么被叫,要么都没有。

在第二种情况下,如果同时修改AtomicBoolean的值,则可以调用它们中的任何一个,或者都不调用它们。

因为Boolean一直存在,并且始终被定义为不可变的,AtomicBoolean(稍后介绍)不能替代Boolean因为它的行为不同而且编码正确如果不可变性被破坏,依赖于Boolean的不变性就会破裂。

请注意,Boolean不能替代AtomicBoolean不能替代。他们的语义并不兼容。

答案 4 :(得分:1)

因为Boolean是不可变的。请参阅:Why Wrapper class like Boolean in java is immutable?和我的回答:

因为2是2.明天不会3

在多线程情况下,永久性首选是默认的,尤其是,它使得更容易阅读和更易于维护的代码。例证:Java Date API,它充满了设计缺陷。如果Date是不可变的,那么API将非常精简。我知道Date操作会创建新日期,并且永远不必查找修改它们的API。

阅读实践中的并发以了解不可变类型的真正重要性。

但请注意,如果由于某种原因您需要可变类型,请使用AtomicInteger AtomicBoolean等。为什么Atomic?因为通过引入可变性,您引入了对线程安全性的需求。如果您的类型保持不变,那么您将不需要哪些,因此在使用可变类型时,您还必须考虑线程安全性并使用concurrent包中的类型。欢迎来到并发编程的精彩世界。

此外,对于Boolean - 我要求您命名一个您可能想要执行的操作,该操作关注布尔值是否可变。设为真吗?使用myBool = true。这是重新分配,而不是变异。否定? myBool = !myBool。同样的规则。请注意,不变性是一个功能,而不是一个约束,所以如果可以提供它,你应该 - 在这些情况下,你当然可以。

注意这也适用于其他类型。整数最微妙的是count++,但这只是count = count + 1,除非你关心以原子方式获取值...在这种情况下使用mutable AtomicInteger

答案 5 :(得分:0)

任何语言中原子操作(有时是封装的)的第一个主要用例是比较和交换语义 - 并发应用程序的基本构建块。

第二个主要用途是隐藏正确放置内存栅栏的复杂性,这是内存模型语义所必需的。

在Java Atomic *中使用特定于平台的本机代码封装上述两者,前者使用volatile关键字封装后者。