Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: added an example of an non-atomic operation that doesn't use a compound assignment

...

This noncompliant code example declares a shared boolean variable flag and uses an optimization in the toggle() method which negates the current value of flag.

Code Block
bgColor#FFcccc

class Flag {
  private boolean flag = true;
 
  public void toggle() {  // unsafe
     flag = !flag; 
  }

  public boolean getFlag() { // unsafe 
    return flag;
  }
}

This solution clearly has a race condition because the value of flag is read, negated, and written.

Alternatively, the assignment operator ^= can be used by the toggle() method to negate the current value of the flag.

Code Block
bgColor#FFcccc
class FooFlag {
  private boolean flag = true;
 
  public void toggle() {  // unsafe
    flag ^= true;  // same as flag = !flag; 
  }

  public boolean getFlag() { // unsafe 
    return flag;
  }
}

However, this code is This solution is also not thread-safe. Multiple threads may not observe the latest state of the flag A race condition exists because ^= constitutes is a non-atomic compound operation.

For Consider, for example, consider two threads that call toggle(). Theoretically the effect of toggling flag twice should restore it to its original value. But the following scenario could occur, leaving flag in the wrong state.

...

Code Block
bgColor#FFcccc
class FooFlag {
  private volatile boolean flag = true;
 
  public void toggle() {  // unsafe
    flag ^= true; 
  }

  public boolean getFlag() { // safe
    return flag;
  }
}

...

Code Block
bgColor#ccccff
class FooFlag {
  private boolean flag = true;
 
  public synchronized void toggle() { 
    flag ^= true; // same as flag = !flag; 
  }

  public synchronized boolean getFlag() { 
    return flag;
  }
}

...

Code Block
bgColor#ccccff
class FooFlag {
  private AtomicBoolean flag = new AtomicBoolean(true);
 
  public void toggle() { 
    boolean temp;
    do {
      temp = flag.get();
    } while(!flag.compareAndSet(temp, !temp));
  }

  public AtomicBoolean getFlag() { 
    return flag;
  }
}

...