Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: general edits

It is tempting to consider primitive Programmers sometimes assume that composite operations on primitive data are atomic. For instance, you can assume that the codeHowever, this is not true in a multithreaded environment. Even code that does not involve a composite operation can be nonatomic, for instance:

Code Block
  x = 3;

is atomic with regard to x. Many people also assume atomicity in operators like the following:

Code Block

  x++;

But this is not an atomic operator; it involves reading the current value of x, adding one to it, and setting x to the new, incremented value.

Declaring a shared mutable variable volatile ensures the visibility of the latest updates on it across This code can be fixed by declaring the variable x as volatile. However, declaring a shared mutable variable volatile ensures the visibility of the latest updates to it across other threads but does not guarantee the atomicity of composite operations. For example, the variable increment operation consisting of the sequence read-modify-write is not atomic even when the variable is declared volatile.

The statement shown below increments the value of x and is nonatomic because it encapsulates several operations - reading the current value of x, adding one to it, and setting x to the new, incremented value.

Code Block

x++;

Wiki Markup
"Sequential consistency and
Wiki Markup
"Sequential consistency and/or freedom from data races still allows errors arising from groups of operations that need to be perceived atomically and are not." \[[JLS 05|AA. Java References#JLS 05]\]. In such cases, the {{java.util.concurrent}} utilities must be used to atomically manipulate a shared variable. If the utilities do not provide the required atomic methods, operations that use the variable should be correctly synchronized. 

...

This rule specifically deals with primitive operators like such as ++. For atomicity of other functionsa grouping of calls to atomic methods of the existing Java API, see CON07-J. Do not assume that a grouping of calls to independently atomic methods is atomic.

The Java Language Specification also permits reads and writes of 64-bit values to be non-atomic; for more information, nonatomic although this is not an issue with most modern JVMs (see CON25-J. Ensure atomicity when reading and writing 64-bit values).

Noncompliant Code Example

In this noncompliant code example, the field itemsInInventory can be accessed by multiple threads. However, when a thread is updating the value of itemsInInventory, it is possible for other threads to read the original value (that is, the value before the update). This is because the post decrement operator is non-atomic.

Code Block
bgColor#FFcccc

private int itemsInInventory = 100;

Code Block
bgColor#FFcccc

private int itemsInInventory = 100;

public int removeItem() {
  if(itemsInInventory > 0) {
    return itemsInInventory--;  // Returns new count of items in inventory
  } 
  return -1; // Error code  
}

Noncompliant Code Example (volatile)

This noncompliant code example attempts to fix the race condition by declaring itemsInInventory as volatile. This guarantees that updates to the field are immediately visible to any thread that reads the field. However, when a thread However, when a thread is updating the value of itemsInInventory, it is still possible for other threads to read the original value (that is, the value before the update). This is because the post decrement operator is non-atomic. nonatomic.

Noncompliant Code Example (volatile)

This noncompliant code example attempts to fix the race condition by declaring itemsInInventory as volatile.

Code Block
bgColor
Code Block
bgColor#FFcccc
private volatile int itemsInInventory = 100;

public int removeItem() {
  if(itemsInInventory > 0) {
    return itemsInInventory--;  // Returns new count of items in inventory
  } 
  return -1; // Error code  
}

}

This guarantees that updates to the field are immediately visible to any thread that reads the field. However, when a thread is updating the value of itemsInInventory, it is still possible for other threads to read the original value (that is, the value before the update). This is because the post decrement operator is nonatomic even when volatile is used.

Compliant Solution (java.util.concurrent.atomic classes)

Volatile variables are unsuitable when more than one load/store operation needs to be atomic. There is an alternative method to perform multiple operations atomically. This compliant solution uses a java.util.concurrent.atomic.AtomicInteger variable.

...

In this noncompliant code example, the two fields a and b may be set by multiple threads, using the setValues() method. While getSum() is always guaranteed to return a sum, that sum might be erroneous.

Code Block
bgColor#FFcccc
private volatile int a,;
private volatile int b;

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

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

For instance, if a and b currently have the value 0, and one thread 1 calls getSum() while thread 2 another calls setValues(1, 1), then getSum() might return 0, 1, or 2. Of these, or 1. The latter case occurs if thread 1 reads a before thread 2 modifies it, and then thread 1 reads b after thread 2 has modified itthe value 1 is unacceptable; it is returned when the first thread reads a and b after the second thread has set the value of a but before it has set the value of b.

Compliant Solution

This compliant solution provides atomicity by method synchronizationsynchronizes the setValues() method so that the entire operation is atomic.

Code Block
bgColor#FFcccc
private volatile int a,;
private volatile int b;

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

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

As in Unlike the previous noncompliant code example, if a and b currently have the value 0, and one thread 1 calls getSum() while thread 2 another calls setValues(1, 1), then getSum() may return return 0, or 2, depending on which thread grabs the intrinsic lock first. But the The locking guarantees that getSum() will not never return the unacceptable value 1.

Risk Assessment

If operations on shared variables are not atomic, unexpected results may be produced. For example, there can be inadvertent information disclosure as one user may be able to receive information about other users.

...