Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: 2 significant CS edits, general edits and fixed text

Wiki Markup
Composite operations on shared variables (consisting of more than one discrete operation) must be performed atomically. Errors can arise from composite operations that need to be perceived atomically but are not \[[JLS 05|AA. Java References#JLS 05]\]. Expressions involving composite operations typically use a combination of operators such as, ++, --, +=, *= and /=.

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.

...

This noncompliant code example contains a data race that may result in the itemsInInventory field failing to account for removed items.

Code Block
bgColor#FFcccc
public class InventoryManager {

  private static final int MIN_INVENTORY = 3;
  private int itemsInInventory = 100;

  public final void removeItem() {
    if (itemsInInventory <= MIN_INVENTORY) {
      throw new IllegalStateException("underUnder stocked");
    }
    itemsInInventory--;
  }
} 

...

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

Code Block
bgColor#FFcccc
public class InventoryManager {

  private static final int MIN_INVENTORY = 3;
  private volatile int itemsInInventory = 100;

  public final void removeItem() {
    if (itemsInInventory <= MIN_INVENTORY) {
      throw new IllegalStateException("under stocked");
    }
    itemsInInventory--;
  }
} 

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 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 post increment composite operation in the returnItem() method is non-atomic.

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

The java.util.concurrent utilities can be used to atomically manipulate a shared variable. This compliant solution defines intemsInInventory as a java.util.concurrent.atomic.AtomicInteger variable, allowing composite operations to be performed atomically.

Code Block
bgColor#ccccff
public class InventoryManager {

  private static final int MIN_INVENTORY = 3;
  private final AtomicInteger itemsInInventory = new AtomicInteger(100);

  private final intvoid removeItem() {
    for (;;) {
      int old = itemsInInventory.get();
      if (old > MIN_INVENTORY) {
        int next = old - 1; // Decrement
        if (itemsInInventory.compareAndSet(old, next)) {
          return next;  // Returns new count of items in inventorybreak;
        }
      } else {
        throw new IllegalStateException("underUnder stocked");
      }
    } // end for
  } // end removeItem()
} 

...

Wiki Markup
The {{compareAndSet()}} method takes two arguments, the expected value of a variable when the method is invoked and the updated value. This compliant solution uses this method to atomically set the value of {{itemsInInventory}} to the updated value if and only if the current value equals the expected value \[[API 06|AA. Java References#API 06]\].  The {{for}} loop guaranteesensures the same behavior of the original function, namely that the function succeeds in decrementingthat the {{removeItem()}} method succeeds in decrementing the most recent value of {{itemsInInventory}} as orlong as anthe errorinventory codecount is returned greater than {{MIN_INVENTORY}}.

Compliant Solution (method synchronization)

...

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
public class InventoryManager {

  private static final int MIN_INVENTORY = 3;
  private int itemsInInventory = 100;

  public final synchronized void removeItem() {
    if (itemsInInventory <= MIN_INVENTORY) {
      throw new IllegalStateException("underUnder stocked");
    }
    itemsInInventory--;
  }
} 

If code is synchronized correctly, updates to shared variables are instantly made visible to other threads. Synchronization is more expensive than using the optimized java.util.concurrent utilities and should generally be preferred when it is sufficiently complex to carry out the operation atomically using the utilities. When synchronizingusing synchronization, care must be taken to avoid deadlocks (see CON12-J. Avoid deadlock by requesting and releasing locks in the same order).

...

Constructors and methods can use block synchronization as an alternative technique called block synchronization which to method synchronization. Block synchronization synchronizes a block of code rather than a method, as shown in this compliant solution.

Code Block
bgColor#ccccff
public class InventoryManager {

  private static final int MIN_INVENTORY = 3;
  private int itemsInInventory = 100;
  private final Object lock = new Object();

  public final synchronized void removeItem() {
    synchronized(thislock) {
      if (itemsInInventory <= MIN_INVENTORY) {
        throw new IllegalStateException("underUnder stocked");
      }
      itemsInInventory--;
    }
  }
} 

Block synchronization is preferable over method synchronization because it reduces the duration for which the lock is held and also protects against denial of service attacks. Block synchronization requires does not require synchronizing on an internal private lock object instead of the intrinsic lock of the class's object. However, it is more secure to synchronize on an internal private lock object instead of a more accessible lock object (see CON04-J. Use the private lock object idiom instead of the Class object's intrinsic locking mechanism).

...

This compliant solution uses a java.util.concurrent.locks.ReentrantLock to atomically perform the post-decrement operation.

Code Block
bgColor#ccccff
public class InventoryManager {

  private static final int MIN_INVENTORY = 3;
  private int itemsInInventory = 100;
  private final Lock lock = new ReentrantLock();

  public final synchronized void removeItem() {
    Boolean myLock = false;

if(lock.tryLock()) {
      try {
      myLock = lock.tryLock();

      if (itemsInInventory <= MIN_INVENTORY) {
      	  throw new IllegalStateException("underUnder stocked");
      	}
      	itemsInInventory--;
      } finally {
      if (myLock) {
        lock.unlock();
      }	
    }

  } // end removeItem()
} 

Code that uses this lock behaves similar to synchronized code that uses the traditional monitor lock. ReentrantLock provides several other capabilities, for instance, the tryLock() method does not block waiting if another thread is already holding the lock. The class java.util.concurrent.locks.ReentrantReadWriteLock can be used when some thread requires a lock to write information while other threads require the lock to concurrently read the information.

...