Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: edits, please review

Wiki Markup
Compound operations onare sharedoperations variablesthat (consistingconsist of more than one discrete operation). mustExpressions bethat performedinclude atomically.postfix Errorsand canprefix arise from compound operations that need to be perceived atomically but are not \[[JLS 05|AA. Java References#JLS 05]\]. 
Expressions that include postfix and prefix increment (++), postfix or prefix decrement (--), or compound assignment operators always result in compound operations. Compound assignment expressions include operators *=, /=, %=, +=, -=, <<=, >>=, >>>=, ^=, or |=.
increment ({{\+\+}}),  postfix or prefix decrement ({{\-\-}}), or compound assignment operators  always result in compound operations.  Compound assignment expressions include operators {{*=, /=, %=, +=, -=, <<=, >>=, >>>=, ^=}} and {{|=}} \[[JLS 05|AA. Java References#JLS 05]\]. Compound operations on shared variables  must be performed atomically to prevent [data races|BB. Definitions#data race] and [race conditions|BB. Definitions#race conditions]. 

For atomicity of a grouping of calls to independently atomic methods belonging to the existing thread-safe classes of the existing Java SE 6 API, see CON07-J. Do not assume that a grouping of calls to independently atomic methods is atomic.

...

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

Code Block
bgColor#FFcccc
final class Flag {
  private boolean flag = true;
 
  public void toggle() {  // unsafeUnsafe
     flag = !flag; 
  }

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

It is prone to a data race because the value of flag is read, negated, and written back.

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

Code Block
bgColor#FFcccc
final class Flag {
  private boolean flag = true;
 
  public void toggle() {  // unsafeUnsafe
    flag ^= true;  // sameSame as flag = !flag; 
  }

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

This solution attempt is also not thread-safe. A data race exists because ^= is a non-atomic compound operation.

Consider, for example, two threads that call toggle(). Theoretically, the The effect of toggling flag twice should is expected to restore it to its original value. However, the following scenario leaves flag in the wrong state:

...

As a result, the effect of the call by t1 2 is not reflected in flag; the program behaves as if the call was never made.

...

This noncompliant code example derives from the preceding one but declares the flag as volatile.

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

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

It is still insecure for multithreaded use because volatile does not guarantee the visibility of updates to the shared variable flag when a compound operation is performed. In other words, the value of the write depends on the current value of the variable.

Compliant Solution (synchronization)

This compliant solution declares both the toggle() and getFlag() methods as synchronized.

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

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

...

It is also permissible to declare flag as volatile to ensure its visibility and while doing so, forgoing synchronization of the getFlag() methodto synchronize the getFlag() method. The toggle() method still requires synchronization because it performs a non-atomic operation.

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

  public boolean getFlag() { 
    return flag;
  }
}

Wiki Markup
The {{toggle()}} method still requires synchronization because it performs a non-atomic operation. However, this This advanced technique is fragile in most other scenarios, such as, when a getter method performs operations other than just returning the value of the {{volatile}} field. The cheap read-write lock trick offers performance advantages because the method to read a value {{getFlag()}} is not synchronized. Unless read performance is critical, this method is not recommended. \[[Goetz 06|AA. Java References#Goetz 06]\]

...

This compliant solution uses the java.util.concurrent.atomic.AtomicBoolean type to declare the flag.

Code Block
bgColor#ccccff
final class Flag {
  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;
  }
}

...

In this noncompliant code example, the two fields a and b may be set by multiple threads, using the setValues() method.

Code Block
bgColor#FFcccc
final class Adder {
  private int a;
  private int b;

  public int getSum() {
    return a + b;
  }

  public void setValues(int a, int b) {
    this.a = a;
    this.b = b;
  }
}

...

Note that declaring the variables as volatile does not resolve the issue because these compound operations involve reads and writes of multiple variables. Also, this code does not prevent integer overflow. See INT00-J. Perform explicit range checking to ensure integer operations do not overflow for more information.

...

The issues described in the previous noncompliant code example can also arise when the fields a and b of type int are replaced with atomic integers.

Code Block
bgColor#FFcccc
final class Adder {
  private final AtomicInteger a = new AtomicInteger();
  private final AtomicInteger b = new AtomicInteger();

  public int getSum() throws ArithmeticException {
    // Check for integer overflow
    if ( b.get() > 0 ? a.get() > Integer.MAX_VALUE - b.get() : a.get() < Integer.MIN_VALUE - b.get() ) {
      throw new ArithmeticException("Not in range");
    }
    return a.get() + b.get(); // or, return a.getAndAdd(b.get());
  }

  public void setValues(int a, int b) {
    this.a.set(a);
    this.b.set(b);
  }
}

For example, when a thread is executing setValues(), another thread may invoke getSum() and retrieve an incorrect result. Furthermore, in the absence of synchronization, there are data races in the check for integer overflow. For instance, a thread can call setValues() after a second thread that is attempting to add the numbers has read a, but before it has read b in order to add them together; in which . In this case, the second thread will get an improper sum.

Even worse, a thread can call setValues() after a second thread has verified that overflow will not occur, but before the second thread reads the values to addbe added. This would cause the second thread to add two values that have not been checked without checking for overflow, and overflow when adding them.Note that even yielding an incorrect sum. Even though a check for integer overflow is installed, there is a it is ineffective because of the time-of-check-time-of-use (TOCTOU) condition between the overflow check and the addition operation.

...

This compliant solution synchronizes the setValues() and getSum() methods so that the entire operation is atomic.

Code Block
bgColor#ccccff
final class Adder {
  private int a;
  private int b;

  public synchronized int getSum() throws ArithmeticException {
    // Check for integer overflow
    if ( b > 0 ? a > Integer.MAX_VALUE - b : a < Integer.MIN_VALUE - b ) {
      throw new ArithmeticException("Not in range");
    }

    return a + b;
  }

  public synchronized void setValues(int a, int b) {
    this.a = a;
    this.b = b;
  }
}

...

Code Block
java
java
public class Foo {
  private boolean flag = true;

  public synchronized boolean toggleAndGet() {
    flag ^= true; // sameSame as flag = !flag;
    return flag;
  }
}

...

Code Block
java
java
@RegionLock("FlagLock is this protects flag")
@Promise("@Unique(return) for new()")
public class Foo {
  private boolean flag = true;

  public synchronized boolean toggleAndGet() {
    flag ^= true; // sameSame as flag = !flag;
    return flag;
  }
}

...

Code Block
java
java
@RegionLock("FlagLock is this protects flag")
public class Foo {
  private boolean flag;

  @Unique("return")
  public Foo() {
    flag = true;
  }

  public synchronized boolean toggleAndGet() {
    flag ^= true; // sameSame as flag = !flag;
    return flag;
  }
}

...

Code Block
java
java
public class Foo {
  @GuardedBy("this")
  private boolean flag = true;

  public synchronized boolean toggleAndGet() {
    flag ^= true; // sameSame as flag = !flag;
    return flag;
  }

  public boolean getValue() {
    return flag;
  }
}

...