What does AtomicBoolean do that a volatile boolean cannot achieve?
They are just totally different. Consider this example of a volatile
integer:
volatile int i = 0;
void incIBy5() {
i += 5;
}
If two threads call the function concurrently, i
might be 5 afterwards, since the compiled code will be somewhat similar to this (except you cannot synchronize on int
):
void incIBy5() {
int temp;
synchronized(i) { temp = i }
synchronized(i) { i = temp + 5 }
}
If a variable is volatile, every atomic access to it is synchronized, but it is not always obvious what actually qualifies as an atomic access. With an Atomic*
object, it is guaranteed that every method is "atomic".
Thus, if you use an AtomicInteger
and getAndAdd(int delta)
, you can be sure that the result will be 10
. In the same way, if two threads both negate a boolean
variable concurrently, with an AtomicBoolean
you can be sure it has the original value afterwards, with a volatile boolean
, you can't.
So whenever you have more than one thread modifying a field, you need to make it atomic or use explicit synchronization.
The purpose of volatile
is a different one. Consider this example
volatile boolean stop = false;
void loop() {
while (!stop) { ... }
}
void stop() { stop = true; }
If you have a thread running loop()
and another thread calling stop()
, you might run into an infinite loop if you omit volatile
, since the first thread might cache the value of stop. Here, the volatile
serves as a hint to the compiler to be a bit more careful with optimizations.