yet_another_programmer
yet_another_programmer

Reputation: 327

Thread-safe negated AtomicBoolean get() as a condition in a while loop

Suppose I have the following code:

AtomicBoolean condition;
condition = new AtomicBoolean(false);
(...)
while(!condition.get()){
   // do some stuff
}

I know that condition.get() is atomic, but, is !condition.get() atomic as well?

I mean, could it happen that one Thread read the boolean value atomically and then it were interrupted before applying the ! operation, so that another Thread gets into the loop before? If that's the case, would it be better to use a function such as:

private synchronized boolean checkNegatedCondition(AtomicBoolean cond){
    return !cond.get();
}

(...)

while(checkNegatedCondition(condition)){
   // do some stuff
}

Thanks in advance.

Upvotes: 4

Views: 3523

Answers (3)

Persixty
Persixty

Reputation: 8589

Atomicity isn't the issue. Two threads can read an atomic variable concurrently. So any number of threads could read the value and enter the loop based on its negation. It doesn't matter if ! is atomic. It acts on a thread-local value.

To use atomics for exclusion you usually need AtomicBoolean.compareAndSet(boolean,boolean).

v.compareAndSet(a,b) will only set v to value b if it is a and return true. Otherwise if v doesn't have the value a at the start, it does nothing and returns false.

In pseudo-code it's

 synchronized public boolean compareAndSet(boolean a,boolean b){
      if(v!=a) return false;
      v=b;
      return true;
 }

But (very important) takes place atomically so all other operations on v can be ordered before or after it.

AtomicBoolean condition = new AtomicBoolean(false);
//...
if(condition.compareAndSet(false,true)){
   //Only one thread can get here unless condition is set back...
}

if you wanted to use this as a 'pseudo' synchronized block you might code:

while(!condition.compareAndSet(false,true));
   //Only one thread can get here at a time...
 condition.set(false);

This could be described as'spin' locking. Because threads 'waiting' to enter the controlled section 'spin' round and round in the loop until released. This may show poorer performance than synchronized because that operating system will normally 'suspend' waiting threads and progress other tasks. However spin-locks can show better performance in cases where contention is very low (i.e. little or no waiting is required).

Pro-Tip: In practice it is good practice to put condition.set(false) in afinally` block:

while(!condition.compareAndSet(false,true));
try {
   //Only one thread can get here at a time...
 }finally{
     condition.set(false);
 }

Otherwise an exception thrown in one thread will permanently lock out any access to that section of code.

Upvotes: 3

GhostCat
GhostCat

Reputation: 140613

One has to be precise here:

  • the result of the get() operation is supposed to be atomic, and because of the "further" guarantees for any atomic class, it is also guaranteed to be "visible" to other threads looking at the same object (atomic in the sense that all the **set* methods are atomic, so it is not possible to get() something that is "half-set")
  • whereas the ! operation is not protected in any way

And just to be sure: the core thing here is not "atomicity" - because a get() just returns a value (and in contrast to a double, there is even just a single bit/byte affected). In other words: AtomicBoolean is mainly about making sure that changes to the object are visible to all threads using the object.

Buty yes, assuming that AtomicBoolean is a field of a class, and multiple threads are calling a method like:

public void foo() {
  while(!condition.get() ...

it is theoretically possible that

  • Thread A "gets" false
  • Thread B "sets" true
  • Thread A computes computes ! false --> true

But: assume that we had a "protected" getNot(), the final result would be the same:

  • Thread A "getNot" false --> true
  • Thread B "sets" true

In both cases, the first thread would enter the loop!

Upvotes: 4

albert_nil
albert_nil

Reputation: 1658

AtomicBoolean.get is not about being atomic, but instead about being thread-safe.

atomic would be a concept related to read and write stuff, and your question does not mention anything like that.

if you want to read and write (or write and read) atomically, there are other methods on AtomicBoolean (like getAndSet or compareAndSet). From your question it might seem you are looking for something more related to that (cannot tell for sure from your question details).

If what you want is not to retrieve and change a value atomically, but instead retrieve until it is changed in other place of your code after some logic (so not atomically), then you are looking for locking.

Upvotes: 0

Related Questions