Examining Volatile Keyword with Java Threads

When we declare a field as volatile, the JVM will ensure that changes to volatile variable are seen by all threads in a consistent manner. In addition, to guaranteed visibility to all threads, using volatile keyword also guarantees atomicity for reads and writes of the variable. That is, using it ensures that any write to the variable will be atomic, so no other thread will ever see a partial write. According to the javadocs, “Reads and writes are atomic for all variables declared volatile (including long and double variables)”. The last guarantee that the volatile keyword gives you comes in the form of ordering. That is, the JVM will try to reduce memory consistency errors by ensuring that any writes to the variable will occur before any subsequent reads of the same variable.

Without using the volatile keyword, the data may be cached locally in CPU cache and as a result changes to the variable by another thread may not be seen by all other threads resulting in inconsistent behavior.

When to use Volatile and when not to

Volatile is not a replacement of synchronized keyword in concurrency programming. Volatile keyword is typically in applications when one thread will be making changes to the variables and the others all reading or consumers of the data. If you have multiple threads making changes to the data it will be best to stick with synchronized block or use java.util.concurrent library package.

It is important to note that volatile keyword does not use locking and blocking will not occur as it does with synchronization blocks.

If you do not have a multithreaded application, do not use volatile and you will incur a performance hit when using it as JVM will not be allowed to optimize the data as it sees fit.

Important Points on Volatile Variables

  • Volatile variables are not cached in registers or in caches
  • Volatile variables are considered lightweight synchronization, but not in anyway meant to replace the synchronized keyword
  • Really meant more for one thread making changes to data and all the other threads working as readers
  • All read and writes are done in main memory, never done thread-locally
  • A good example of using volatile variables are for status flags used in spin loops
  • Volatile keyword guarantees visibility and ordering

ThreadVolatileExample.java

package com.avaldes.tutorials;

public class ThreadVolatileExample {
  private static ThreadData data = new ThreadData(); 

  public static void main(String[] args) {
    
    System.out.println("Starting all three reader threads..");
    for (int i = 1; i<= 3; i++) {
      Thread t = new Thread(new ReaderThread(data), "ReaderThread_" + i);
      t.start();
    }
  
    System.out.println("Waiting 2 secs on main thread...");
    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    } 
    
    Thread writer = new Thread(new WriterThread(data), "WriterThread");
    writer.start();
  }
}

ReaderThread.java

package com.avaldes.tutorials;

public class ReaderThread implements Runnable {
  private final ThreadData td;
  
  public ReaderThread(ThreadData td) {
    this.td = td;
  }

  public void run() {
    long myCount = td.getCounter();

    while (td.getCounter() < ThreadData.MAX_COUNT) { 
      if (myCount != td.getCounter()) {
        myCount = td.getCounter();
        System.out.format("Thread %s, count is %d...\n", 
             Thread.currentThread().getName(), myCount);
      }
    }
    System.out.println("Exiting run() for " + Thread.currentThread().getName());
  } 
}

WriterThread.java

package com.avaldes.tutorials;

public class WriterThread implements Runnable {
  private final ThreadData td;
  private long currentCount;
  
  public WriterThread(ThreadData td) {
    this.td = td;
  }
  
  public void run() {
    currentCount = td.getCounter();
    
    for (int i = 1; i<= 5; i++) {
      try {
        ++currentCount;
        td.setCounter(currentCount); 
        System.out.format("\nThread %s, count is %d...\n", 
             Thread.currentThread().getName(), currentCount);
        Thread.sleep(500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

ThreadData.java

package com.avaldes.tutorials;

public class ThreadData {
  public final static int MAX_COUNT = 5;
  private volatile long counter = 0; 

  public long getCounter() {
    return counter;
  }

  public void setCounter(long counter) {
    this.counter = counter;
  }
}

Output using Volatile Keyword

Starting all three reader threads..
Waiting 2 secs on main thread...
Thread ReaderThread_2, count is 1...

Thread WriterThread, count is 1...
Thread ReaderThread_1, count is 1...
Thread ReaderThread_3, count is 1...

Thread WriterThread, count is 2...
Thread ReaderThread_2, count is 2...
Thread ReaderThread_3, count is 2...
Thread ReaderThread_1, count is 2...

Thread WriterThread, count is 3...
Thread ReaderThread_2, count is 3...
Thread ReaderThread_1, count is 3...
Thread ReaderThread_3, count is 3...

Thread WriterThread, count is 4...
Thread ReaderThread_1, count is 4...
Thread ReaderThread_2, count is 4...
Thread ReaderThread_3, count is 4...

Thread WriterThread, count is 5...
Thread ReaderThread_1, count is 5...
Thread ReaderThread_2, count is 5...
Thread ReaderThread_3, count is 5...

Exiting run() for ReaderThread_2
Exiting run() for ReaderThread_1
Exiting run() for ReaderThread_3

Try it Out and Make changes in ThreadData.java

Now go ahead and remove the volatile keyword from line 5 such that code will look like below example. When you are done with this quick change, rerun the application and see what happens…

package com.avaldes.tutorials;

public class ThreadData {
  public final static int MAX_COUNT = 5;
  private long counter = 0; 

  public long getCounter() {
    return counter;
  }

  public void setCounter(long counter) {
    this.counter = counter;
  }
}

Output Without using Volatile Keyword

As you can see in the following execution, when the volatile keyword has been removed from the ThreadData class the ReaderThread class fails to see the latest changes to the counter and as a result we do not see any output from the ReaderThread. Also, as a result of the lack to visibility the ReaderThread does not exit the while loop after WriterThread has completed…. You will need to kill the java process from the command line as the process will go into a busy wait condition and CPU will rise.

Starting all three reader threads..
Waiting 2 secs on main thread...

Thread WriterThread, count is 1...

Thread WriterThread, count is 2...

Thread WriterThread, count is 3...

Thread WriterThread, count is 4...

Thread WriterThread, count is 5...

Related Posts

  • Java Thread, Concurrency and Multithreading Tutorial
    This Java Thread tutorial will give you a basic overview on Java Threads and introduce the entire tutorial series on concurrency and multithreading. From here, you will learn about many java thread concepts like: Thread States, Thread Priority, Thread Join, and ThreadGroups. In addition, you will learn about using the volatile keyword and examples on using wait, notify and notifyAll.
  • Java Thread States - Life Cycle of Java Threads
    Get a basic understanding of the various thread states. Using the state transition diagram we show the various states for a Java thread and the events that cause the thread to jump from one state to another.
  • Creating Java Threads Example
    In this post we cover creating Java Threads using the two mechanisms provided in Java, that is, by extending the Thread class and by implementing Runnable interface for concurrent programming.
  • Java Thread Priority Example
    In this post we cover Thread priorities in Java. By default, a java thread inherits the priority (implicit) of its parent thread. Using the setPriority() method you can increase or decrease the thread priority of any java thread.
  • Java ThreadGroup Example
    Sometimes we will need to organize and group our threads into logical groupings to aid in thread management. By placing threads in a threadGroup all threads in that group can be assigned properties as a set, instead of going through the tedious task of assigning properties individually.
  • Java Thread Sleep Example
    We seem to use this method very often to temporarily suspend the current threads execution for a specific period of time. Let's spend some time and familiarize ourselves with what this method actually does.
  • Java Thread Join Example
    In Java, using Thread.join() causes the current thread to wait until the specified thread dies. Using this method allows us to impose an order such that we can make one thread wait until the other completes doing what it needed to do, such as completing a calculation.
  • Examining Volatile Keyword with Java Threads
    When we declare a field as volatile, the JVM will guarantee visibility, atomicity and ordering of the variable. Without it the data may be cached locally in CPU cache and as a result changes to the variable by another thread may not be seen by all other threads resulting in inconsistent behaviour.
  • Java Threads Wait, Notify and NotifyAll Example
    The purpose of using notify() and notifyAll() is to enable threads to communicate with one another via some object on which to performing the locking. A thread using the wait() method must own a lock on the object. Once wait() is called, the thread releases the lock, and waits for another thread to either call notify() or notifyAll() method.
  • Java Thread Deadlock Example and Thread Dump Analysis using VisualVM
    Deadlock is a condition where several threads are blocking forever, waiting for the other to finish but they never do. This tutorial will discuss situations that will lead to Java Thread deadlock conditions and how they can be avoided. In addition, we will discuss using Java VisualVM to pinpoint and analyze the source of the deadlock conditions.
  • Java Thread Starvation and Livelock with Examples
    Starvation occurs when a thread is continually denied access to resources and as a result it is unable to make progress. Thread liveLock is a condition that closely resembles deadlock in that several processes are blocking each other. But with livelock, a thread is unable to make any progress because every time it tries the operation always fails.
  • Java Synchronization and Thread Safety Tutorial with Examples
    One of Java's many strengths come from the fact that it supports multithreading by default as has so from the very onset. One of the mechanisms that Java uses for this is via synchronization. When we use the synchronized keyword in Java we are trying limit the number of threads that can simultaneously access and modify a shared resource. The mechanism that is used in Java's synchronization is called a monitor.
  • Creating a Thread Safe Singleton Class with Examples
    In this tutorial we cover many examples of creating thread-safe singleton classes and discuss some of the shortfalls of each and provide some recommendations on best approaches for a fast, efficient and highly concurrent solution.
  • Java Threads and Concurrent Locks with Examples
    In this tutorial we will focus primarily on using the concurrent utilities and how these can make concurrent programming easier for us.

Please Share Us on Social Media

Facebooktwitterredditpinterestlinkedinmail

Leave a Reply

Your email address will not be published. Required fields are marked *