Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The Java Language Specification also permits reads and writes of 64-bit values to be non-atomic. (For more information, see CON25-J. Ensure atomicity when reading and writing 64-bit values.).

Noncompliant Code Example (

...

Logical Negation)

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

...

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

...

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

Noncompliant Code Example (

...

Bitwise Negation)

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

...

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

Noncompliant Code Example (volatile)

Declaring flag as volatile also does not help either:

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

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

This code remains unsuitable for multithreaded use because declaring a variable as volatile does not guarantee the atomicity of compound operations on that variableit.

Compliant Solution (

...

Synchronization)

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

...

The second execution order involves the same operations, just that but t2 starts and finishes before t1.

Compliance with the guideline CON04-J. Synchronize using an internal private final lock object can reduce the likelihood of misuse by ensuring that untrusted callers cannot access the lock object.

Compliant Solution (

...

Volatile-

...

Read,

...

Synchronized-

...

Write)

In this compliant solution, the getFlag() method is not synchronized, and flag is declared as volatile. This solution is compliant because the read of flag in the getFlag() method is an atomic operation and the volatile qualification assures visibility. The toggle() method still requires synchronization because it performs a non-atomic operation.

...

Wiki Markup
This approach may not be used when a getter method performs operations other than just returning the value of a {{volatile}} field without having to use any synchronization. Unless read performance is critical, this technique may not offer significant advantages over synchronization \[[Goetz 06|AA. Java References#Goetz 06]\].

The volatile-read, synchronized-write pattern is also addressed in CON11-J. Do not assume that declaring an object reference volatile guarantees visibility of its members also addresses the volatile-read, synchronized-write pattern.

Compliant Solution (

...

Read-

...

Write Lock)

This compliant solution uses a read-write lock to ensure atomicity and visibility.

...

Wiki Markup
Read-write locks allow a shared state to be accessed by multiple readers or a single writer, but never both. "In practice, read-write locks can improve performance for frequently accessed read-mostly data structures on multiprocessor systems; under other conditions they perform slightly worse than exclusive locks due to their greater complexity." \[[Goetz 06|AA. Java References#Goetz 06]\]. The Profiling the application can determine the suitability of read-write locks can be determined by profiling the application.

Compliant Solution (AtomicBoolean)

This compliant solution declares flag as an AtomicBoolean type.

Code Block
bgColor#ccccff
import java.util.concurrent.atomic.AtomicBoolean;

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

The flag variable is updated using the compareAndSet() method of the AtomicBoolean class AtomicBoolean. All updates are visible to other threads.

Noncompliant Code Example (

...

Addition)

In this noncompliant code example, multiple threads may invoke the setValues() method may be invoked by multiple threads to set the a and b fields. Because this class does not test for integer overflow, a user of the Adder class must ensure that the arguments to the setValues() method can be added without overflow. (For more information, see INT00-J. Perform explicit range checking to ensure integer operations do not overflow for more information.

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

The getSum() method contains a data race. For example, if a and b currently have the values 0 and Integer.MAX_VALUE, respectively, and one thread calls getSum() while another calls setValues(Integer.MAX_VALUE, 0), then getSum() might return 0, Integer.MAX_VALUE, or it might overflow and wrap. Overflow will occur when the first thread reads a and b, after the second thread has set the value of a to Integer.MAX_VALUE but before it has set the value of b to 0.

...