Versions Compared

Key

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

Wiki Markup
Composite operations consisting of more than one discrete operation are, by definition, non-atomic. For example, the Java expression {{x++}} is non-atomic because it is a composite operation consisting of three discrete operations: reading the current value of {{x}}, adding one to it, and writing the new, incremented value back
to x.
Wiki Markup
"Sequential consistency and/or freedom from data races still allows errors arising from groups of to {{x}}.  Errors can arise from composite operations that need to be perceived atomically andbut are not." \[[JLS 05|AA. Java References#JLS 05]\]. In such cases, the {{java.util.concurrent}} utilities can be used to atomically manipulate a shared variable. If these utilities do not provide the required functionality in the form of atomic methods, operations that use the variable should be correctly synchronized. 

Note that, as with volatile, updated values are immediately visible to other threads when either of these two techniques is used. Synchronization provides a way to safely share object state across multiple threads without the need to reason about reorderings, compiler optimizations and hardware specific behavior.

This rule specifically deals with primitive operators such as ++. For atomicity of a grouping of calls to independently 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 though this is not an issue with most modern JVMs (see CON25-J. Ensure atomicity when reading and writing 64-bit values).

Noncompliant Code Example (post-decrement composite operation)

This noncompliant code example contains a data race that may result in the itemsInInventory field being incorrectly decremented.

For atomicity of a grouping of calls to independently 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 though this is not an issue with most modern JVMs (see CON25-J. Ensure atomicity when reading and writing 64-bit values).

Noncompliant Code Example (post-decrement composite operation)

This noncompliant code example contains a data race that may result in the itemsInInventory field being incorrectly decremented.

Code Block
bgColor#FFcccc

private int itemsInInventory = 100;

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

private int itemsInInventory = 100;

public final int removeItemreturnItem() {
  if (itemsInInventory > 0== Integer.MAX_VALUE) { // Check for integer overflow
    return itemsInInventory--1;  // Returns new count of items in inventoryError Code
  } else {
    return -1; // Error code  
}itemsInInventory++;
  }
}

For example, if the removeItem() method is concurrently invoked by two threads, the execution of these threads may be interleaved so that:

...

As a result, a decrement operation is "lost" and the itemsInInventory value is now incorrect.

Similarly, a the returnItem() method that increments itemsInInventory is also nonatomic: non-atomic.

Noncompliant Code Example (volatile)

This noncompliant code example attempts to resolve the problem by declaring itemsInInventory as volatile.

Code Block
bgColor#FFcccc

private volatile int itemsInInventory = 100;
Code Block
bgColor#FFcccc
public final int returnItemremoveItem() {
  if itemsInInventory++;
}

Noncompliant Code Example (volatile)

This noncompliant code example attempts to resolve the problem by declaring itemsInInventory as volatile.

Code Block
bgColor#FFcccc

private volatile int itemsInInventory = 100;(itemsInInventory > 0) {
    return itemsInInventory--;  // Returns new count of items in inventory
  } 
  return -1; // Error code  
}

public final int removeItemreturnItem() {
  if (itemsInInventory > 0) {== Integer.MAX_VALUE) { // Check for integer overflow
    return itemsInInventory--1;  // ReturnsError newCode
 count of} itemselse in inventory{
  } 
  return -1itemsInInventory++;
 // Error code   }
}

Volatile variables are unsuitable when more than one read/write operation needs to be atomic. The use of a volatile variable in this noncompliant code example guarantees that once itemsInInventory has been updated, the new value is immediately visible to all threads that read the field. However, because the post decrement operator is nonatomic, even when volatile is used, the interleaving described in the previous noncompliant code example is still possible.

Similarly, the returnItem() method does not perform the increment operation atomically.

...

bgColor#FFcccc

...

.

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

The java.util.concurrent utilities can be used to atomically manipulate a shared variable. This compliant solution uses a {java.util.concurrent.atomic.AtomicInteger}} variable which allows certain composite operations to be performed atomically.

...

It is necessary to use a local variable temp because there is a time-of-check to time-of-use (TOCTOU) condition between checking whether the inventory count is less than Integer.MAX_VALUE and using getAndIncrement() to increment it. Notably, this functionality could also be implemented by using the compareAndSet() method. The getAndIncrement() alternative is useful when control over setting the returned value must lie in the hands of the caller instead of the invoked method (returnItem()).

...

the invoked method (returnItem()).

Note that updated shared variables are visible to other threads.

Compliant Solution (method synchronization)

Synchronization provides a way to safely share object state across multiple threads without the need to reason about reorderings, compiler optimizations, and hardware specific behavior.

This compliant solution uses method synchronization to synchronize access to itemsInInventory. Consequently, access to itemsInInventory is mutually exclusive and its state consistent across all threads.

Code Block
bgColor#ccccff
private final int itemsInInventory = 100;

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

similarly, the returnItem() method can be fixed by synchronizing it:

Code Block
bgColor#ccccff
public final synchronized int returnItem() {
  if (itemsInInventory == Integer.MAX_VALUE) { // Check for integer overflow
    return -1; // Error Code
  } else {
    return itemsInInventory++;
  }
}

Synchronization is more expensive than using the optimized java.util.concurrent utilities and should only be used when the utilities do not contain the facilities (methods) required to carry out the atomic operation. When synchronizing, care must be taken to avoid deadlocks (see CON12-J. Avoid deadlock by requesting and releasing locks in the same order).

Note that updates to correctly synchronized shared variables are visible to other threads.

Compliant Solution (block synchronization)

...