As part of java concurrency, it is introduced some new datatype for concurrency handling. Those are called atomic datatypes. Here in this article, we are exploring those data types.
What are Atomic Datatypes?
In Java under the package java.util.concurrent.atomic Contains the classes such as AtomicBoolean, AtomicInteger, AtomicLong, AtomicReference, AtomicLongArray…etc. these classes are called atomic data types. These classes having the methods such as to get(), set(int value), lazySet(int value), compareAndSet(int expect, int update) Which used to read and write on values on the data types.
The Atomic classes are in effect equivalent to volatile + synchronized even though the implementation is different. Inside if you look at the implementation of atomic classes source you can see it is not initialized any synchronization but those are synchronized that's why atomic variables defined lock-free thread-safe programming on single variables. Inside the atomic classes, the values are stored in a volatile variable.
What are the different Atomic Datatypes?
All of the atomic data types are defined inside the package
java.util.concurrent.atomic. Here follows the details of different data types inside the package
AtomicInteger: It is used to read and write
int values atomically. It contains atomic methods such as
NOTE: All these datatypes contains common methods such as
get()used for getting the value and
set()used for setting the value.
AtomicBoolean: It is used to read and write
boolean value atomically. It contains atomic methods such as
AtomicIntegerArray: The class provides operations on underlying int array that can be read and written atomically, and also contains advanced atomic operations such as
AtomicIntegerFieldUpdater: It is a reflection-based utility that enables atomic updates to designated
volatile int fields of designated classes. It contains methods such as
AtomicMarkableReference: AtomicMarkableReference is a generic class that encapsulates both a reference to an Object and a boolean flag. These two fields are coupled together and can be updated atomically, either together or individually. It contains methods such as
AtomicReference class provides an object reference variable that can be read and written atomically. By atomic is meant that multiple threads attempting to change the same
AtomicReferencewill not make the
AtomicReference end up in an inconsistent state. AtomicReference contains methods such as
long values, it contains the datatypes such as AtomicLong, AtomicLongArray, AtomicLongFieldUpdater.
For AtomicReference it contains additional data types such as AtomicReferenceArray, AtomicReferenceFieldUpdater and AtomicStampedReference.
When we can use Atomic Datatypes? Explain With Examples?
We know what is atomic variables. But what is its use in the real world? Let's explore this with an example.
In the below example I initialized two thread each thread are adding one lack with a normal variable, volatile variable, and an atomic variable in a for a loop.
Here is the result of the above program execution.
result value -> 182462
volatileResult value -> 179853
atomicResult value -> 200000
There the correct result is printed by atomic datatype which is two lack. Here the atomic data type class prints the correct result because the operation
incrementAndGet() cannot be interfered with by multiple threads.
The operations inside atomic data type classes are cannot interfere by multiple threads. A situation, where multiple threads are repeatedly modifying data types can use with atomic data types. Else we need to handle those situations with synchronization but it may affect the performance.
What is the difference between atomic datatypes, synchronization, and volatile variable?
The volatile variables are modifying their value immediately affects the actual memory storage for the variable. The compiler cannot optimize away any references made to the variable. This guarantees that when one thread modifies the variable, all other threads see the new value immediately.
atomic variable guarantees that operations made on the variable occur in an atomic fashion, i.e., that all of the substeps of the operation are completed within the thread they are executed and are not interrupted by other threads. For example, an increment-and-test operation requires the variable to be incremented and then compared to another value; an atomic operation guarantees that both of these steps will be completed as if they were a single indivisible/uninterruptible operation.
Synchronizing all accesses to a variable allows only a single thread at a time to access the variable, and forces all other threads to wait for that accessing thread to release its access to the variable.
Synchronized access is similar to atomic access, but the atomic operations are generally implemented at a lower level of programming. Also, it is entirely possible to synchronize only some accesses to a variable and allow other accesses to be unsynchronized (e.g., synchronize all writes to a variable but none of the reads from it).
Atomicity, synchronization, and volatility are independent attributes but are typically used in combination to enforce proper thread cooperation for accessing variables.