Learnitweb

volatile keyword in Java

Marking a variable as volatile is a weaker form of synchronization. Volatile variable always returns the most recent value of the variable written by any thread as volatile variables are not cached in registers or in caches.

Volatile variables may look like to behave like Synchronized classes (for example, SynchronizedInteger), but it is not. Synchronized classes (for example, SynchronizedInteger) are more stronger form of synchronization.

So we can say that volatile variables take care of the memory visibility issues associated with the simultaneous read and write of variables. For the sake of understanding, we can say that:

  • Writing a volatile variable by a thread is similar to exiting a synchronized block.
  • Reading a volatile variable by a thread is similar to entering a synchronized block.

What problem does volatile solve?

Processors, compiler and runtime do some optimizations (like caching and reordering) while compiling and running code. When code does not have proper synchronization, these optimizations can create issues. Volatile is a way to control memory order.

Processors are responsible for executing code. For this the code and other required data from the main memory is fetched. Processors use caching so that the data is not written and read from main memory. This improves performance.

However, this could create issues. The changes in the cache are not written back to main memory instantly. This depends on the write policy of cache. The delay may be much longer and the values may not be same in two cache and the main memory. So it is possible that all threads have different values of sharedVariable as shown in the diagram.

To solve this problem, volatile could be helpful. Marking the variable as volatile ensures that the variable is read and written from the main memory.

Use of volatile variables

Volatile variables are commonly used to represent status flag and indicate important lifecycle events.

We must not rely too much on volatile variables from memory visibility perspective. Instead, we should use synchronization. Code which relies on volatile heavily has more chance of breaking at some point of time and it is not readable and easy to understand.

volatile variable can not ensure atomicity, it can only ensure visibility. If you have to use volatile variable, you should consider using atomic classes with volatile. This will ensure both atomicity and visibility.

Volatile variable should be used when:

  • it is not used in any code used to update state with other state variables.
  • it is ensured that only one thread writes to the variable and other synchronization is not required to access the variable.
  • write to the variable does not depend on current value.

If multiple threads do not access a variable, there is no need to make it volatile.

Can volatile make a non-atomic operation to atomic?

As mentioned earlier, volatile can not ensure atomicity, it can only ensure visibility. However, there are cases when use of volatile can make read and write operation as atomic.

For example, both long and double are of 64-bit and are read in two parts 32-bit each. While reading or writing a long it is possible that one thread is reading one half of the long and the other thread is writing the other half. Using a volatile long will ensure that the read and write are atomic.

Happens before ordering

The memory visibility effects of volatile variables extend beyond the volatile variables themselves. Any write to a volatile variable happens before every subsequent read of the same field.

Piggybacking

A non volatile variable can rely on the happens before assurance provided by the volatile variable and can exhibit volatile behavior. By using piggybacking, we can declare less variables as volatile and still optimize the visibility guarantee as provided by the volatile.

volatile variable example in Java

To understand volatile in Java, let us see the code for double checked locking in Singleton.

public class Singleton {
	private static volatile Singleton _instance;

	public static Singleton getInstance() {
		if (_instance == null) {
			synchronized (Singleton.class) {
				if (_instance == null)
					_instance = new Singleton();
			}
		}
		return _instance;
	}
}

If we do not make the _instance as volatile, after creating the _instance if thread loses the CPU and other threads get a chance to run, then other threads will still see value of _instance as null. We have marked _instance as volatile, this will ensure that all threads see the same value of _instance.

Another example of volatile

private volatile boolean conditionFlag; 

while(!conditionFlag) { 
	// code to be executed based on condition 
}

In this code, we are using a conditionFlag to check and execute code. If we don’t mark conditionFlag as volatile, it is possible that other threads could still see old value even after the change in value. This could result in other threads executing the code block if they see the value as false. When the flag is changed, it should be instantly be visible to other threads. That is why this variable becomes a very good candidate of being marked as volatile.

Important points

  • The volatile keyword cannot be used with classes or methods.
  • Using volatile variables reduces the risk of memory consistency errors, because any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable. It means that changes to a volatile variable are always visible to other threads.
  • Every read of a volatile variable will be read from the computer’s main memory, and not from the CPU cache. Similarly, every write to a volatile variable will be written to main memory, and not to the CPU cache.
  • Reads and writes are atomic for all variables declared volatile. (including long and double variables).
  • The access to the volatile variable is non-blocking.
  • Java volatile variable that is an object reference may be null.