37

The Oracle JDK Javadoc for AtomicBoolean states:

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

A boolean value that may be updated atomically. See the java.util.concurrent.atomic package specification for description of the properties of atomic variables. An AtomicBoolean is used in applications such as atomically updated flags, and cannot be used as a replacement for a Boolean.

A colleague and I were trying to figure out a use-case where the AtomicBoolean can't be a substitute and the only thing we can think of is that there are methods the Boolean object has that the AtomicBoolean does not.

Is that the only reason or was there something else in mind when that was written?

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
Brooks
  • 7,099
  • 6
  • 51
  • 82
  • 9
    `Boolean` is immutable; by definition, `AtomicBoolean` is not. – JimmyB Apr 29 '16 at 15:11
  • 2
    One thing I havent seen on any answers: performance. Anything atomic and thread-safe is always going to be less performant than its 'vanilla' alternative. – David says Reinstate Monica Apr 29 '16 at 16:40
  • 1
    Yeah I'd agree with @DavidGrinberg - an AtomicBoolean is slower than a straight Boolean or primitive boolean. Sometimes, you really don't care about the extras – Jon Story Apr 29 '16 at 17:21
  • Note: Everything said on this page applies equivalently to `AtomicInteger` vs `Integer` and `AtomicLong` vs `Long`. – Boann Apr 29 '16 at 19:00
  • @David Well that's only true because Java requires class wrappers (Scala on the other hand already allows value types to some degree). A `struct AtomicInteger` should have exactly the same performance as a primitive as can be seen in other languages. This does not change the fact that this would be an inherently bad idea: `AtomicBoolean` *documents* the intention of the programmer. It should be rarely used and therefore stand out when you see it. For the same and other reasons C++ uses `std::atomic` although they could easily extend `bool` without performance problems. – Voo Apr 30 '16 at 10:28

6 Answers6

58

Boolean is an immutable value object. It was designed to be unchanging and made final in order to enforce that. java.lang.Boolean has been around since 1.0.

AtomicBoolean is mutable, and designed to get updated so that the updated value is visible across threads. AtomicBoolean was introduced with Java 5.

These are entirely different concepts, which is why AtomicBoolean wasn't designed to extend Boolean. You can't substitute a mutable object for an immutable one without wrecking the expected invariants of the code using it. Code expecting to receive an immutable value could get broken if the atomic version could be passed in in its place.

So here's a use case: if AtomicBoolean was introduced as something that was substitutable for Boolean, you could have a case where a class created before this change could reasonably expect that in some method that returns a Boolean it doesn't need to pass a defensive copy on account of Boolean being immutable. If the reference returned happens to get initialized from a source that changes to use AtomicBoolean instead of Boolean, then that field could now be modified by things calling the method returning Boolean, by casting it to AtomicBoolean.

The atomic classes are designed for working with concurrent updates (as an improvement on volatile), but the most efficient way to design concurrent code is to use immutable values. So be careful not to mistake AtomicBoolean for "the Boolean you use when writing multithreaded code".

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
  • @JimmyB Not really, it's not the only reason they're not substitutable. – Dave Newton Apr 29 '16 at 15:14
  • 1
    I would articulate this as `AtomicBoolean` is an atomic *reference* to a boolean. The only reason it exists at all is because `AtomicReference` is not a legal type and `AtomicReference` has overheads (and slightly different semantics). – Derek Elkins left SE Apr 30 '16 at 00:42
37

Boolean is the wrapper class around the primitive boolean. It may be automatically created from a boolean by the compiler (boxing conversion) or converted to a boolean (unboxing conversion). This is not the case for AtomicBoolean where it is a separate class designed for concurrency purposes.

Hence the two classes have different semantics at the language level:

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
M A
  • 71,713
  • 13
  • 134
  • 174
  • 4
    This, along with Dave's answer is what answered our question for us. It felt like trying to decide whom to donate money to...a billionaire or multi-billionaire, so ultimately it came down to the explanatory nature and I saw this one first. – Brooks Apr 29 '16 at 15:43
4

They're not auto-boxable, so they can't be used in conditionals, e.g.,

// Explodey
if (someAtomicBoolean) {
}
Dave Newton
  • 158,873
  • 26
  • 254
  • 302
  • 2
    Sure, but *why* isn't it auto-boxed? – JimmyB Apr 29 '16 at 15:09
  • @JimmyB Because it's not a Boolean? Because that's the way they wrote Java. The question was why can't it be used as a Boolean; this (and the subclassing thing) is why. Only the language designers and implementors can provide their reasoning. Technically you could define autoboxing rules for anything, e.g., a custom app class *could* have a mechanism for autoboxing into boolean expressions. Why doesn't Java do that? Because Java doesn't do that. – Dave Newton Apr 29 '16 at 15:11
  • That's not a valid argument. The question remains, why didn't they make `AtomicBoolean` a `Boolean`. Nathan Hughes gave the appropriate answer: Because they *behave* differently and cannot be handled the same way without possibly creating conflicts. - Notice that auto-unboxing does only work on immutable types in Java. – JimmyB Apr 29 '16 at 15:15
  • 1
    @JimmyB The two reasons they're not substitutable are orthogonal. The question from the OP, which is what we're answering, was related to "figuring out] a use-case where the AtomicBoolean can't be a substitute". I provided one. *That* is the question that was asked, not "why was Java written this way". – Dave Newton Apr 29 '16 at 15:19
  • Maybe I over-interpreted the question when I focused on "the only thing we can think of is that [...] Boolean [can do things] AtomicBoolean [cannot]". This thought is indeed correct or not, depending on whether or not you see *immutability* as additional functionality. So the correct answer is: The difference is "immutability", and because that's neither more nor less than "mutability" the conclusion is just that both are simply not compatible. – JimmyB Apr 29 '16 at 15:27
  • @JimmyB But... there are other differences that makes them unsubstitutable based on Java-language-that-actually-exists. – Dave Newton Apr 29 '16 at 15:28
  • If they're not purely syntactic then please tell us what they are. – JimmyB Apr 29 '16 at 15:30
  • @JimmyB What makes a "purely syntactic" reason an invalid reason? The inheritance issue isn't "purely syntactic". Neither is this one; it's not syntax, it's language semantics. Whether or not you think it's good or bad Java was written this way, it *was* written this way, and is one of the reasons they're not substitutable. I'm not even sure how this is even up for discussion, since it's one of the reasons they're not. – Dave Newton Apr 29 '16 at 15:34
  • Sorry, but anything a compiler can deterministically validate *is* syntax. The compiler does not know about semantics. As such, when the compiler errors out because of incompatible types or, much more, when it determines it has to generate auto-(un)boxing code, we're talking about pure syntax. Not concepts, not semantics, not logic, not rationale. I think that the syntactical differences between `Boolean` and `AtomicBoolean` are pretty clear (and/or can be read from the source code). But these differences merely *reflect* the behavioral/semantic differences between the two. – JimmyB May 02 '16 at 14:49
  • @JimmyB Syntax has a specific meaning, which isn't semantics. A language's *syntax*, its grammar, allows output that's not *semantically* valid. In fact, fuzz-testing based on generated syntactically-correct source code is used to test language semantics. The grammar/syntax of Java is well-defined. Not all syntactically-valid code is semantically valid. It's syntax that disallows using an `AtomicBoolean` in an `if` statement. It's *not* syntax, e.g., that warns about class-casting issues, that's semantics. – Dave Newton May 02 '16 at 14:59
  • I don't quite get your notion of "language semantics". Still, all the compiler has available is the *syntax* of the source code. `String str = new File("");` is *syntactically* invalid because, syntactically verifiably, `File` is not declared to be a subclass of `String`. *Warnings* issued by the compiler show the limits of what's syntactically verifiable: The compiler can use heuristics and try to point out things that *may* be issues, but it cannot decide if each of them actually *is* a problem or if it's intended as written. Only the semantics of the whole program at runtime can tell that. – JimmyB May 02 '16 at 15:08
  • (Or the programmer with an "educated guess" and a `@SuppressWarnings`...) – JimmyB May 02 '16 at 15:08
  • Of course the compiler has more than the syntax-that doesn't even make any sense. A language is more than its syntax. But no, it's not syntactically invalid-variable declarations consist of modifiers, type, name, and an optional expression. It's Java's type rules that make the statement invalid. The parsing is an earlier step than typing. – Dave Newton May 02 '16 at 15:11
  • I admit I'm coming at this with an extensive compiler background. You're conflating syntax with semantics. These are almost *always* separate steps, for a variety of reasons. Please look at the java grammar. – Dave Newton May 02 '16 at 15:13
  • "variable declarations consist of modifiers, type, name, and an optional expression" - Java is not, and never claimed to be, a *regular* (Type-3) language. It's a *context sensitive* (Type-1) language. Hence, it cannot be described by any type of regular (expression) grammar. That's why e.g. `int x = 0; int x = 1;` is not valid Java code, just as the assignment from above is not valid Java. - A simple compiler does not have to know *anything* about Java semantics to get the job done. A smart compiler knows *some* semantics and so may be able to optimize away things like `x = x + 1; x = x - 1;` – JimmyB May 02 '16 at 15:27
  • Exactly. Syntactic validity is different than semantics, and syntactically valid programs may not be semantically valid. But a simple compiler would have the opposite problem-it wouldn't know *enough* about semantics to prevent runtime errors when they could be caught at compile time. – Dave Newton May 02 '16 at 15:28
4

Example:

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();
  }

}

can give a different result than

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();
  }

}

because an AtomicBoolean can change its value while a Boolean cannot.

In the first case, do1() and do2() are either both called or none of them.

In the second case, both, either, or none of them may be called if the AtomicBoolean's value is modified concurrently.

Because Boolean has always been there, and was always defined as immutable, AtomicBoolean, which was introduced much later, cannot be substitutable for Boolean because it behaves differently and code that rightfully relies on the immutability of a Boolean can break if that immutability is destroyed.

Notice that Boolean cannot be substituted for AtomicBoolean and vice versa. They're just not compatible in their semantics.

JimmyB
  • 12,101
  • 2
  • 28
  • 44
  • The concurrent "capabilities" of the AtomicBoolean wasn't really what we were going after, rather the implications of trying to use AtomicBoolean instead of a Boolean in a non-concurrent application (though the non-concurrent application was admittedly not part of the original question). – Brooks Apr 29 '16 at 15:41
  • Ok :) But how should the poor Java compiler know that you're actually not going to use the `AtomicBoolean` concurrently in your specific application? It can't, and hence it cannot allow you to use an `AtomicBoolean` instead of a `Boolean` or vice versa. – JimmyB Apr 29 '16 at 15:43
  • @JimmyB Autoboxing itself doesn't present a concurrency issue; it would just call`get()`, the way you'd do it manually, like how the autoboxing code calls `java/lang/Boolean.booleanValue`. – Dave Newton Apr 29 '16 at 15:47
  • @DaveNewton Sorry, I shouldn't have used auto-unboxing in the example as it apparently distracts from the actual point; I edited the post. – JimmyB Apr 29 '16 at 15:54
  • 1
    Still, the contract for auto-unboxing is only defined for *immutable* objects. So a *mutable* object cannot be unboxed without breaking the semantics of boxing. – JimmyB Apr 29 '16 at 15:57
1

Because Boolean is immutable. See: Why Wrapper class like Boolean in java is immutable? and my answer:


Because 2 is 2. It won't be 3 tomorrow.

Immutable is always preferred as the default, especially in multithreaded situations, and it makes for easier to read and more maintainable code. Case in point: the Java Date API, which is riddled with design flaws. If Date were immutable the API would be very streamlined. I would know Date operations would create new dates and would never have to look for APIs that modify them.

Read Concurrency in Practice to understand the true importance of immutable types.

But also note that if for some reason you want mutable types, use AtomicInteger AtomicBoolean, etc. Why Atomic? Because by introducing mutability you introduced a need for threadsafety. Which you wouldn't have needed if your types stayed immutable, so in using mutable types you also must pay the price of thinking about threadsafety and using types from the concurrent package. Welcome to the wonderful world of concurrent programming.

Also, for Boolean - I challenge you to name a single operation that you might want to perform that cares whether Boolean is mutable. set to true? Use myBool = true. That is a re-assignment, not a mutation. Negate? myBool = !myBool. Same rule. Note that immutability is a feature, not a constraint, so if you can offer it, you should - and in these cases, of course you can.

Note this applies to other types as well. The most subtle thing with integers is count++, but that is just count = count + 1, unless you care about getting the value atomically... in which case use the mutable AtomicInteger.

Community
  • 1
  • 1
djechlin
  • 59,258
  • 35
  • 162
  • 290
0

The first main use case of atomic operations (sometimes encapsulated) in any language is compare-and-swap semantics - a fundamental building blocks of the concurrent applications.

The second main use is to hide the complexity of correctly placing memory fences, required for the memory model semantics.

In Java Atomic* encapsulate both of the above, the former - with the platform specific native code, and the latter with the help of volatile keyword.

bobah
  • 18,364
  • 2
  • 37
  • 70
  • 1
    I didn't downvote, but a virtual machine can emulate anything it wants--that's kind of the point of a virtual machine. – Dave Newton Apr 29 '16 at 16:02
  • @DaveNewton - It's not the VM, it's the compiler, that cannot read programmers mind and thus does nothing about detecting `if (!x) { dosomething; x = true;}` race conditions automatically. – bobah Apr 29 '16 at 16:09
  • 1
    I'm saying I disagree with the statement "compare-and-swap is impossible to emulate in software" as a blanket statement--clearly it's possible, e.g., a CPU emulator for a CPU that has a CaS instruction. – Dave Newton Apr 29 '16 at 16:20
  • @DaveNewton I reformulated the first paragraph – bobah Apr 29 '16 at 16:42
  • Compare-and-swap is *used* to implement locks to implement critical sections &c. In Java, the "fundamental building block" for concurrency is the `synchronized` statement, which the Atomics usually leverage. – JimmyB Apr 29 '16 at 17:33
  • 1
    @JimmyB - Do you see `synchronized` [in AtomicBoolean.java](http://www.docjar.com/html/api/java/util/concurrent/atomic/AtomicBoolean.java.html)? – bobah Apr 29 '16 at 17:35