April 25, 2024

Java Volatile Keyword With Examples

In this post we’ll see what is volatile keyword in Java, when to use volatile variable and what is the reason for declaring variable as volatile.

What is volatile keyword in Java

To know volatile keyword in Java better you would have to know a little about the optimizations that happens for the variables with in the Java memory model. Let’s say you have a variable test declared in your code. You would think that test variable will be stored only in RAM and all the threads will read the value of the test variable from there. But the processors, in order to make processing faster, would hold the value of the variable in its cache. In that case any change to the value is written back to the main memory only when the synchronization between the cache and the memory happens.

That will cause problem where multiple threads are reading or writing a shared variable. If we take our example of test variable which is used among multiple threads, there may be a scenario that one thread has made changes to test variable which is still stored in cache and another thread tries to read the value of test variable from the main memory. That will result in memory inconsistency errors as different threads will read/write different values of test variable.

volatile keyword in java

How declaring variable as volatile in Java helps

Declaring a variable as volatile ensures that value of the variable is always read from the main memory. So declaring a field as volatile in Java gives visibility guarantee that a write to a volatile field happens-before every subsequent read of that field.

Problem we saw above because of value being cached by the CPU will not happen with volatile field as it is guaranteed that updates done by thread-1 to the volatile variable will always be visible to thread-2.

Volatile Java example code

One of the most common use of the volatile keyword in Java is boolean status flags declared as volatile, where the flag indicates completion of event so that another thread can start.

Let’s first see what will happen if volatile is not used in such case.

public class VolatileDemo {
  private static  boolean flag = false;
  public static void main(String[] args) {
    // Thread-1
    new Thread(new Runnable(){
      @Override
      public void run() {
        for (int i = 1; i <= 2000; i++){
          System.out.println("value - " + i);
        }
        // changing status flag
        flag = true;
        System.out.println("status flag changed " + flag );
      }			
    }).start();
    // Thread-2
    new Thread(new Runnable(){		
      @Override
      public void run() {
        int i = 1;
        while (!flag){
          i++;
        }
        System.out.println("Start other processing " + i);    
      }
    }).start();
  }
}
Output
....
....
value - 1997
value - 1998
value - 1999
value - 2000
status flag changed true

On running this code you will see that the first thread displays value of i till 2000 and change the status flag but the second thread won’t print the message "Start other processing " and the program won’t terminate. Since flag variable is accessed frequently in the thread-2 in the while loop, the compiler may optimize by placing the value of flag in a register, then it will keep testing the loop condition (while (!flag)) without reading the value of flag from main memory.

Now if you change the boolean variable flag and mark it as volatile that will guarantee that the change done to the shared variable by one thread is visible to other threads.

private static volatile boolean flag = false;
Output
....
....
value - 1997
value - 1998
value - 1999
value - 2000
status flag changed true
Start other processing 68925258

Volatile also ensures reordering of statements doesn’t happen

When a thread reads a volatile variable, it not only sees the latest change to the volatile, but also the side effects of the code that led up the change. That is also known as the happens before extended guarantee which is provided by volatile keyword from Java 5.

For example, If thread T1 changes other variables before updating the volatile variable then thread T2 will get the updated variable of those variables too that were changed before the update of volatile variable in thread T1.

That brings us to the point of reordering that may happen at compile-time for optimizing the code. The code statements may be reordered as long as the semantic meaning is not changed.

private int var1;
private int var2;
private volatile int var3;
public void calcValues(int var1, int var2, int var3){
  this.var1 = 1;
  this.var2 = 2;
  this.var3 = 3;
}

Since var3 is volatile so, because of happens-before extended guarantee, updated values of var1 and var2 will also be written to main memory and visible to other threads.

What if these statements are re-ordered for optimization.

this.var3 = 3;
this.var1 = 1;
this.var2 = 2;

Now the values of variables var1 and var2 are updated after update of volatile variable var3. So the updated values of these variables var1 and var2 may not be available to other threads.

That is why reordering is not permitted if read or write of volatile variable happens after the update to other variables.

Volatile ensures visibility not atomicity

In the scenario where only one thread is writing to a variable and other thread is just reading (like in case of status flag) volatile helps in the correct visibility of the value of the variable. But volatile is not enough if many threads are reading and writing the value of the shared variable. In that case because of race condition threads may still get wrong values.

Let's clear it with a  Java example in which there is a class SharedData whose object is shared among the thread. With in the SharedData class counter variable is marked as volatile. Four threads are created that increment the counter and then display the updated value. Because of the race condition threads may still get wrong values. Note that you may get the correct values also in few runs.

public class VolatileDemo implements Runnable {
  SharedData obj = new SharedData();
  public static void main(String[] args) {
    VolatileDemo vd = new VolatileDemo();
    new Thread(vd).start();
    new Thread(vd).start();
    new Thread(vd).start();
    new Thread(vd).start();
  }

  @Override
  public void run() {
    obj.incrementCounter();
    System.out.println("Counter for Thread " + Thread.currentThread().getName() + 
        " " + obj.getCounter());
  }	
}

class SharedData{
  public volatile int counter = 0;
  public int getCounter() {
    return counter;
  }

  public void incrementCounter() {
    ++counter;
  }
}
Output
Counter for Thread Thread-0 1
Counter for Thread Thread-3 4
Counter for Thread Thread-2 3
Counter for Thread Thread-1 3

Important points about volatile in Java

  • Volatile keyword in Java can only be used with variables not with methods and classes.
  • A variable marked as volatile ensures that the value is not cached and the updates to the volatile variables are always done in main memory.
  • Volatile also ensures that the reordering of the statements don’t happen that way volatile provides happens-before extended guarantee where changes to other variables before the update of volatile variables are also written to main memory and visible to other threads.
  • Volatile ensures just visibility not the atomicity.
  • It is a compile-time error if a final variable is also declared volatile.
  • Using volatile is less expensive than using lock.

That's all for the topic Java Volatile Keyword With Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment