I am a Java Developer working in Sydney. I am eager to learn new technologies, new frameworks, new languages. Thibault has posted 8 posts at DZone. You can read more from them at their website. View Full User Profile

Java volatile keyword explained by example

06.21.2012
| 91715 views |
  • submit to reddit

Overview

volatile is probably the less known/understood/documented keyword in Java. I have recently read an article on one of my favourite blog about the volatile keyword. The author shows a piece of code where the volatile keyword seems to have an influence. This example was not easy to understand and the role of the volatile keyword on the behaviour of the JVM was not really defined. So I have decided to browse the web to find a better code example for the volatile keyword. After one hour, nothing! Only wrong examples, articles comparing volatile with synchronized and other confused examples where the author seems as lost as the reader...

Basic Example

The following show a basic example where volatile is required

public class VolatileTest {
    private static final Logger LOGGER = MyLoggerFactory.getSimplestLogger();

    private static volatile int MY_INT = 0;

    public static void main(String[] args) {
        new ChangeListener().start();
        new ChangeMaker().start();
    }

    static class ChangeListener extends Thread {
        @Override
        public void run() {
            int local_value = MY_INT;
            while ( local_value < 5){
                if( local_value!= MY_INT){
                    LOGGER.log(Level.INFO,"Got Change for MY_INT : {0}", MY_INT);
                     local_value= MY_INT;
                }
            }
        }
    }

    static class ChangeMaker extends Thread{
        @Override
        public void run() {

            int local_value = MY_INT;
            while (MY_INT <5){
                LOGGER.log(Level.INFO, "Incrementing MY_INT to {0}", local_value+1);
                MY_INT = ++local_value;
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) { e.printStackTrace(); }
            }
        }
    }
}

With the volatile keyword the output is :

Incrementing MY_INT to 1
Got Change for MY_INT : 1
Incrementing MY_INT to 2
Got Change for MY_INT : 2
Incrementing MY_INT to 3
Got Change for MY_INT : 3
Incrementing MY_INT to 4
Got Change for MY_INT : 4
Incrementing MY_INT to 5
Got Change for MY_INT : 5 

Without the volatile keyword the output is :

Incrementing MY_INT to 1
Incrementing MY_INT to 2
Incrementing MY_INT to 3
Incrementing MY_INT to 4
Incrementing MY_INT to 5
.....And the change listener loop infinitely... 

 

Explanation

So what happens? Each thread has its own stack, and so its own copy of variables it can access. When the thread is created, it copies the value of all accessible variables in its own memory. The volatile keyword is used to say to the jvm "Warning, this variable may be modified in an other Thread". Without this keyword the JVM is free to make some optimizations, like never refreshing those local copies in some threads. The volatile force the thread to update the original variable for each variable. The volatile keyword could be used on every kind of variable, either primitive or objects! Maybe the subject of another article, more detailed...

Never used volatile and never met this problem...

Like all threads issues, it happens under specials circumstances. Really special for this one... My example has big chances to show mainly because the ChangeListener thread is busy, thanks to the loop, and the JVM consider that this thread has no time for updating the local variables. Executing some synchronized methods or adding an other variable which is volatile (or even executing some simple lines of code) could modify the JVM behavior and "correct" this problem...

Should I do a big refactor to identify all variables who needs volatile?

 Be pragmatic! If you think your project needs it, do it. I think that the essential is to be aware of that, to know what is the goal of each keyword of the java language in order to take the good decisions.

 

Please find my source code on my github repository

Published at DZone with permission of its author, Thibault Delor. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Balázs Bessenyei replied on Fri, 2012/06/22 - 4:24pm

Good article however I would like to make a few additions.

The above example and behavior is only true for JDK 1.5 and up.

Volatile is noway a replacement of synchronized keyword. It only deals with shared data visibility between threads. 

The typical use case is simple signaling between threads. One thread sets the signal and the others are only read it.  

It will not solve the problem caused by multiple thread updating the same variable, even if it is declared volatile.

It is not safe to use variables marked with it to generate unique ids from multiple threads (++ and -- are not atomic). Use the Atomic wrapper classes (AtomicInteger,  AtomicLong and AtomicReference) instead.

The example code is using the signaling ability, however it is easy to mistake it as safe unique id generator solution.

Thibault Delor replied on Fri, 2012/06/22 - 10:55pm in response to: Balázs Bessenyei

Oh, I did't know for JDK 1.4! Anyway I hope that no one is still using the JDK4 (I mean the JVM, not the language...). Good point though.

I did not want to speak about synchronized in this article to keep it short. I will probably talk about synchronized and atomic types/operations in a following article.

 Thanks for your constructive comment Balázs ! 

Wojciech Kudla replied on Sun, 2012/06/24 - 12:34pm

You also forgot to mention word-tearing and non-atomic 64-bit types treatment which seems to be an underestimated problem. Doubles and longs are not that uncommon similarly to 32-bit platforms. As a lot of deployments are still running on x86 platforms the issue still stands.

Jose Maria Arranz replied on Mon, 2012/06/25 - 2:23am

One of the most interesting uses of volatile is for simple Double Checked Locking
// Works with acquire/release semantics for volatile
// Broken under Java 1.4 and earlier semantics for volatile
class Foo {
    private volatile Helper helper = null;
    public Helper getHelper() {
        Helper result = helper;
        if (result == null) {
            synchronized(this) {
                result = helper;
                if (result == null) {
                    helper = result = new Helper();
                }
            }
        }
        return result;
    }
 
    // other functions and members...
}

M M Islam Chisty replied on Wed, 2013/03/20 - 1:57am

Try without using volatile (with JDK7), it will still produce the same result; with or without volatile, does not matter, the result will be same. Try it.

Mukul Kumar replied on Fri, 2013/04/12 - 9:13am

I agree with the above comment that running under JDK 7 the result is the same with or without volatile. Can some one please clarify the issue here.  

Mustafa Asif replied on Thu, 2013/12/26 - 10:41pm

Hi,

Thanks for the article and reproducible code.

I just want to confirm my understanding of why this code reproduces the problem in absence of volatile keyword. It is because the change listener thread never updates it's local_value to the latest MY_INT because of the JVM optimizations and in presence of the keyword it will keep refreshing local_value as the value of MY_INT changes. Right?

If that's right - it means the effect of volatile keyword is actually not on MY_INT that is declared volatile but on the variable(s) that read it's values, right?

Please respond as this will help me read about this further.

Thanks in advance,

Mustafa

Parag Dummy replied on Mon, 2014/03/24 - 5:20am in response to: M M Islam Chisty

Hi there... I am also facing the same. I have run this program with and without the volatile keyword with jdk 7. Both are giving the same result ...

Can anyone explain it 


thanks...

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.