Volatile and atomic variables are an important feature in Java language. Especially when dealing with multi threading. A simple incrementation algorithm works fine in a single threaded program, but won’t work as expected in a multi threaded system:
private int counter = 0; public int increment(int i) { return i++; }
This code will work correctly in a single threaded program, but will give incorrect results in case of a multi threaded execution. How do we fix it? With the help of Java volatile and atomic variables.
Contents
What is the difference between volatile and atomic variables? In short – a volatile variable is a variable, which can be changed from anywhere at any time, and thus cannot be optimized away. One consequence is that all the threads see the changes in them at once, but don’t actually set a lock on them. Atomic variables, on the other hand, are guaranteed to update all the threads and don’t allow to change their values asynchronously.
Volatile variables are those variable which are not optimized by the compiler while parsing the code. Volatile indicates that this variable value may change every time. A volatile variable is used when a program is interfacing with hardware or directly with the operating system. It means the volatile variable is not stored in the cache. If we try to access a volatile variable twice, it will be accessed twice from the hardware memory, not the cache. In this way, volatile variable avoids optimization.
Read also: Java conversions and 7 Golden rules
Let us take a look at a simple example. We write a program that checks if the printer is running or not. In a simple one-threaded program, we declare a boolean printer_running variable and see if its still true:
boolean printer_running = true; while(printer_running) { //do something }
In a multi-threaded system this code may not work correctly. If some other thread accesses the variable and changes it to false, the thread running this code block may not know about the change immediately. This may result in the loop being executed more than necessary and may result in data integrity problems and it is a hard very hard error to find and fix. A solution to this problem may be to declare the printer_running variable as volatile. This will make all the changes to the variable by one thread be visible to all other threads at once.
volatile boolean printer_running = true; while(printer_running) { //do something }
This is an example of volatile variable without synchronization. This means that the variable can still be accessed by other threads while the code performs changes to it.
In order for the code to be bullet-proof we need to synchronize the block performing operations on volatile variables:
class Foo { private volatile boolean printer_running = true; public boolean getStatus() { if (printer_running) { synchronized(this) { while(printer_running) { //do something } } } return printer_running; }
This code is an example of a double-checked locking or volatile variable with synchronization. This code ensures that no other thread gets to execute the while() block while at the same time all other threads get to know new updated values of the volatile variable.
Atomic variables are variables that use a low-level CPU operations. It provides non-blocking variable change by multiple threads. The compound operations with such variables are performed automatically without the use of synchronization. The memory effect is the same as volatile variable. The most important advantage of an atomic variable is that it avoids deadlocks.
Read also: Recipe for a high-quality “equals” method
Atomic boolean variables are boolean variables that can be updated atomically. These variables don’t cause deadlocks and at the same time perform synchronized between threads. Atomic booleans are a good choice when a program operates with different threads that can access the boolean value simultaneously.
boolean initialized = false; if (!initialized) { initialize(); initialized = true; }
This code is not thread safe. If a thread changes the value of initialized Boolean, i.e is initialized = true, the whole program gets affected. Atomic Boolean can rescue us from this situation:
AtomicBoolean atomicInitialized = new AtomicBoolean (); if (atomicInitialized.compareAndSet(false, true)) { initialize(); }
Compare and set is a function defined in Java, which compare 2 values and then swap them. The atomic initialized bool variable check the value. If it is true, then it is turned false and immediately returned in the memory so that result remains same for all variables.
Atomic reference is another techniques used in Java to do thread safe operations on the reference. The main purpose of atomic referencing is also synchronization.
AtomicReference<Object> cache = new AtomicReference<Object>(); Object cachedValue = new Object(); cache.set(cachedValue); Object cachedValueToUpdate = cache.get(); //... do some work to transform cachedValueToUpdate into a new version Object newValue = someFunctionOfOld(cachedValueToUpdate); boolean success = cache.compareAndSet(cachedValue,cachedValueToUpdate);
As you can see, that due to atomic referencing, the synchronizations are done without using a synchronized keyword. It saves resources and synchronization is a costly process.
Read also: Java test with JUnit
Atomic integer is also provided in Java which provides and integer variable which can be read and write properly. It can be accessed by many threads concurrently. It also provides compare and swap instruction support.
private volatile int counter; public int getNextUniqueIndex() { return counter++; }
In this example, multiple threads can change the counter variable and data integrity is affected. Below is the example of using Atomic Integer:
private AtomicInteger counter; public int getNextUniqueIndex() { return counter.getAndIncrement(); }
The beauty of atomic int is that it is tread safe and it cannot violate the thread safety because it achieves same synchronization as in atomic boolean, hence get the correct result even multiple threads access it at the same time.
Senior Software Engineer developing all kinds of stuff.