...
Pre- and post-increment and pre- and post-decrement operations in Java are non-atomic in that the value written depends upon the value initially read from the operand. For example, 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
.
This noncompliant code example contains a data race that may result in the itemsInInventory
field being incorrectly decrementedmissing the fact that callers returned or removed items.
Code Block | ||
---|---|---|
| ||
public class InventoryManagement { private static final int MIN_INVENTORY = 10; private static final int MAX_INVENTORY = 500; private int itemsInInventory = 100; public final intvoid removeItem() { if (itemsInInventory > 0<= MIN_INVENTORY()) { return itemsInInventory--; throw // Returns new count of items in inventory IllegalStateException("under stocked"); } return itemsInInventory--1; // Error code } public final intvoid returnItem() { if (itemsInInventory == Integer.MAX_VALUEINVENTORY) { // Check for integer overflow throw new return -1; // Error Code } returnIllegalStateException("over stocked"); } itemsInInventory++; } } |
For example, if the removeItem()
method is concurrently invoked by two threads, t1 and t2, the execution of these threads may be interleaved so that:
...
Time | itemsInInventory= | Thread | Action |
---|---|---|---|
1 | 100 | t1 |
reads the current value of |
...
, 100, into a temporary variable | |||
2 | 100 | t2 | reads the current value of |
3 | 100 | t1 | decrements the temporary variable to 99 |
4 | 100 | t2 | decrements the temporary variable to 99 |
5 | 99 | t1 | writes the temporary variable value to |
6 | 99 | t2 | writes the temporary variable value to |
As a result, the effect of the call by t1 is not reflected in itemsInInventory
. It is as if the call was never made. This "lost call" phenomenon can occur with concurrent calls to returnItem()
or concurrent calls to removeItem()
and returnItem()
As a result, a decrement operation is "lost" and the itemsInInventory
value is now incorrect.
Similarly, the returnItem()
method that increments itemsInInventory
is also non-atomic.
Noncompliant Code Example (volatile)
...