In Java volatile keyword is used to guarantee memory visibility of a variable. When a variable is marked as volatile, it will be stored in main memory. Every read & write is performed on main memory instead of CPU cache.
In case of without volatile instance variable, read/write are always performed on CPU cache not directly on main memory, in such cases when multiple threads are performing read/write on multiprocessor system there are possibilities that the variable is updated in CPU cache but not in main memory as JVM take some time to push changes to main memory and some threads may get non-updated value (by other thread) from their own CPU cache. Remember, every thread in Java has it’s own thread local stack at runtime.
Volatile keyword guarantees visibility of changes across threads. Using volatile is a way to make your class thread-safe like synchronized and atomic wrapper. It means the volatile variable can be used by multiple threads for read/write at the same time without any problem.
Synchronize vs Volatile
In concurrency there are 2 important concepts:
1. Atomicity (Mutual Exclusion): It means only one thread can update the value, by executing critical section.
2. Visibility: It means that value updated by one thread to shared data is visible to other thread.
Synchronized keyword guarantees both atomicity and Visibility. In synchronized code block which updates the value, only one thread can enter the code block (critical section) and the update will be reflected in main memory, all the other thread will be blocked and put to sleep.
In some cases, we may only need visibility, not atomicity, in those cases volatile can be used, as using synchronized will cause performance issues because it allows only one thread to work other will be blocked.
In Java volatile usage is limited to very restricted use cases. I tried to capture most of the use cases here:
1. Boolean flag
A most common use case of volatile is a simple boolean flag, setting up the value to true/false in one thread and multiple threads are accessing that variable, and where write doesn’t depend upon existing/current value. Ex: terminating a thread based on flag value.
2. One thread writes and other reads
Volatile can be used when only one thread is writing the value and other only reading its value.
3. Double-checked locking mechanism
Used often in Singleton design pattern. In this, the singleton object needs to be declared volatile.
As volatile write/read are performed on the main memory, read/write on main memory is more expensive than accessing it from CPU cache.
Accessing volatile variable also prevent instruction reordering which is a normal performance enhancement technique.