Protecting Data with Atomic Classes

With the release of Concurrency API Java added a new java.util.concurrent.atomic package to help coordinate access to primitive values and object references. As with most classes in the Concurrency API, these classes are added solely for convenience.

For instance, the increment operator ++ is not thread-safe. The reason that it is not thread-safe is that the operation is not atomic, carrying out two tasks, read and write, that can be interrupted by other threads. That leads to so called lost update. Lost update occurs when one thread overwrites changes made by another thread.

Atomic is the property of an operation to be carried out as a single unit of execution without any interference by another thread. A thread-safe atomic version of the increment operator would be one that performed the read and write of the variable as a single operation, not allowing any other threads to access the variable during the operation.

Using atomics does not imply blocking access to data on JVM level (as with synchronized or locks). Atomics exploit low-level atomic machine instructions, such as compare-and-swap (CAS). CAS works in a loop mode. Typical CAS operation works on three operands :

  1. The memory location on which to operate (M)
  2. The existing expected value (A) of the variable
  3. The new value (B) which needs to be set

The CAS operation updates atomically the value in M to B, but only if the existing value in M matches A, otherwise no action is taken. Each thread according to the algorithm above does the following :

  1. Creates a local copy of the shared data
  2. Modifies the local copy as needed
  3. When ready, updates the shared data by swapping it with the local copy created before

Point 3. is the key : the swap is performed atomically through an atomic operation. That is why the algorithm above is called lock-free algorithm – in lock-free algorithm threads get in touch only during that tiny atomic swap, running undisturbed and unaware of others for the rest of the time. If swapping has failed (local copy value of the shared data did not match the value in shared data, i. e. the value in shared data was changed by another thread), then algorithm repeats, and repeats until swap is successful. It is a spin until success strategy, also called spin lock.

The Concurrency API includes useful classes that are conceptually the same as primitive classes but that support atomic operations. Using compare-and-swap (CAS) is tightely coupled with data it operates on, so each of atomic  classes is tied with the type of data :

Class Name Description
AtomicBoolean A boolean value that may be updated atomically
AtomicInteger An int value that may be updated atomically
AtomicIntegerArray An int array in which elements may be updated atomically
AtomicLong A long value that may be updated atomically
AtomicLongArray A long array in which elements may be updated atomically
AtomicReference A generic object reference that may be updated atomically
AtomicReferenceArray An array of generic object references in which elements may be updated atomically

Each of the classes above includes numerous methods that are equivalent to many of the primitive built-in operators that are used on primitives, such as the assignment operator = and the increment operator ++. See table below :

Method Name Description
get() Retrieve the current value
set() Set the given value, equivalent to the assignment = operator
getAndSet() Atomically sets the new value and returns the old value
incrementAndGet() For numeric classes, atomic pre-increment operation equivalent to ++value
getAndIncrement() For numeric classes, atomic post-increment operation equivalent to value++
decrementAndGet() For numeric classes, atomic pre-decrement operation equivalent to –value
getAndDecrement() For numeric classes, atomic post-decrement operation equivalent to value–

Leave a Reply

Your email address will not be published. Required fields are marked *