繁体   English   中英

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

[英]Why can't AtomicBoolean be a replacement for Boolean?

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没有。

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

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

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

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

所以这是一个用例:如果AtomicBoolean是作为可替代布尔值的东西引入的,那么你可能会遇到这样一种情况:在这个改变之前创建的一个类可以合理地期望在一些返回布尔值的方法中它不需要通过防御由于布尔值是不可变的而复制。 如果返回的引用恰好从更改为使用AtomicBoolean而不是Boolean的源进行初始化,那么现在可以通过调用返回布尔值的方法修改该字段,方法是将其转换为AtomicBoolean。

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

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

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

// Explodey
if (someAtomicBoolean) {
}

例:

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反之亦然 它们的语义不兼容。

因为Boolean是不可变的。 请参阅: 为什么Java中的Wrapper类与Boolean一样是不可变的? 和我的回答:


因为2是2.明天不会是3

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

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

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

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM