Core Java

Volatile Keyword Java Example

In this tutorial, we will discuss the volatile keyword in Java. When a field is declared as volatile, then, the Java Memory Model ensures that all threads will “see” the same consistent value.

In Java, global ordering is imposed on the reading and write operations concerning a volatile variable. A thread that access a volatile field, will first read its current value from the main memory, instead of using a potential cached value. A write operation to a volatile variable establishes a happens-before relationship with all subsequent reads of that volatile variable. Therefore, any change to a volatile variable is always visible to other threads.
 

1. The volatile keyword in Java

In the following example, we demonstrate a sample example of how to use a volatile variable.

VolatileData.java

01
02
03
04
05
06
07
08
09
10
11
12
public class VolatileData {
     
    private volatile int counter = 0;
     
    public int getCounter() {
        return counter;
    }
     
    public void increaseCounter() {
        ++counter;
    }
}

Initially, we declare a new class, called VolatileData that has a private integer field, declared as volatile. The class supports two methods, getCounter and increaseCounter, which return the current value and increase the current value by one, respectively.

VolatileThread.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
public class VolatileThread extends Thread {
    private final VolatileData data;
     
    public VolatileThread(VolatileData data) {
        this.data = data;
    }
     
    @Override
    public void run() {
        int oldValue = data.getCounter();
        System.out.println("[Thread " + Thread.currentThread().getId()
                + "]: Old value = " + oldValue);
         
        data.increaseCounter();
         
        int newValue = data.getCounter();
        System.out.println("[Thread " + Thread.currentThread().getId()
                + "]: New value = " + newValue);
    }
}

In addition, we declare a new class, called VolatileThread, which receives an instance of the VolatileClass in its constructor. Then, during its execution, the thread prints the current value of the volatile variable, increases it by one and finally, prints the updated value of the volatile variable.

VolatileMain.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
public class VolatileMain {
     
    private final static int TOTAL_THREADS = 2;
     
    public static void main(String[] args) throws InterruptedException {
        VolatileData volatileData = new VolatileData();
         
        Thread[] threads = new Thread[TOTAL_THREADS];
        for(int i = 0; i < TOTAL_THREADS; ++i)
            threads[i] = new VolatileThread(volatileData);
         
        //Start all reader threads.
        for(int i = 0; i < TOTAL_THREADS; ++i)
            threads[i].start();
         
        //Wait for all threads to terminate.
        for(int i = 0; i < TOTAL_THREADS; ++i)
            threads[i].join();
    }
}

Inside the main method we start two threads that have access to the same volatile variable. A sample execution is shown below:

1
2
3
4
[Thread 10]: Old value = 0
[Thread 11]: Old value = 0
[Thread 10]: New value = 1
[Thread 11]: New value = 2

As we observe, initially, both threads print the same value. The second thread accesses and prints the latest value of the volatile variable, after both increaseCounter() operations have been applied.

2. Need for volatile

The Java programming language allows threads to access shared variables. As a rule, to ensure that shared variables are consistently and reliably updated, a thread should ensure that it has exclusive use of such variables by obtaining a lock that, conventionally, enforces mutual exclusion for those shared variables.

A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable. One common example of using volatile is to use a volatile boolean variable as a flag to terminate a thread. If you’ve started a thread, and you want to be able to safely interrupt it from a different thread, you can have the thread periodically check a flag.

3. When to use Volatile

Declaring a variable volatile thus guarantees the visibility for other threads of writes to that variable. In Java, we can not have synchronized variable. Using synchronized keyword with a variable is illegal and will result in compilation error. Instead of using the synchronized variable in Java, you can use the java volatile variable, which will instruct JVM threads to read the value of volatile variable from main memory and don’t cache it locally.

If a variable is not shared between multiple threads then there is no need to use the volatile keyword.

4. Code

In this section, we will see how volatile works. Let’s assume we have 3 cores in the systems. Each core will have its own L1 and L2 cache. These cores will communicate with RAM via L3 cache and the bus.

Volatile java - Memory Model
Figure 1. Memory Model

Now if the two threads run in different cores there is a possibility that the results are inconsistent as the threads can cache the results in there respective cache.

Let’s create a class and define a static class variable:

private static int value = 0;

Now we will define two threads one for reading the value and the other for writing it. In the Reader class, we will first create a local variable and will initialize its value to the global value. Then in a loop, we will check if the local value is the same as the global one, if not then we will print the new global value.

int local_value = value;
while (local_value < 10) {
    if (local_value != value) {
        System.out.println(format("Global value has changed to: %s", value));
        local_value = value;
    }
}

Below is the writer thread:

int local_value = value;
while (value < 10) {
    System.out.println(format("Incrementing global value to %s", local_value + 1));
    value = ++local_value;
    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

When we run this we will see result as below:

Incrementing global value to 1
Incrementing global value to 2
Incrementing global value to 3
Incrementing global value to 4
Incrementing global value to 5
Incrementing global value to 6
Incrementing global value to 7
Incrementing global value to 8
Incrementing global value to 9
Incrementing global value to 10

Now if we make the global variable volatile

Incrementing global value to 1
Global value has changed to: 1
Incrementing global value to 2
Global value has changed to: 2
Incrementing global value to 3
Global value has changed to: 3
Incrementing global value to 4
Global value has changed to: 4
Incrementing global value to 5
Global value has changed to: 5
Incrementing global value to 6
Global value has changed to: 6
Incrementing global value to 7
Global value has changed to: 7
Incrementing global value to 8
Global value has changed to: 8
Incrementing global value to 9
Global value has changed to: 9
Incrementing global value to 10
Global value has changed to: 10

When we made the variable volatile it is read from the main memory and is not cached via individual thread.

VolatileExample.java

package com.javacodegeeks;

import static java.lang.String.format;

public class VolatileExample {

    private volatile static int value = 0;

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

    static class DataReader extends Thread {
        @Override
        public void run() {
            int local_value = value;
            while (local_value < 10) {
                if (local_value != value) {
                    System.out.println(format("Global value has changed to: %s", value));
                    local_value = value;
                }
            }
        }
    }

    static class DataWriter extends Thread {
        @Override
        public void run() {
            int local_value = value;
            while (value < 10) {
                System.out.println(format("Incrementing global value to %s", local_value + 1));
                value = ++local_value;
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

5. More about the volatile keyword

While using the volatile keyword in your application’s code, you must be aware of the following:

  • The volatile keyword does not eliminate the need of synchronization among atomic actions, because memory consistency errors are still possible.
  • The usage of atomic variables is more efficient compared to the access through synchronized code, but it requires an extra effort by the programmer, in order for memory inconsistency errors to be avoided.
  • The volatile keyword is not a replacement of a synchronized block or method.

6. Download the Eclipse Project

Download
You can download the full source code of this example here: Volatile Keyword Java Example

Last updated on Jun. 30th, 2020

Sotirios-Efstathios Maneas

Sotirios-Efstathios (Stathis) Maneas is a PhD student at the Department of Computer Science at the University of Toronto. His main interests include distributed systems, storage systems, file systems, and operating systems.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button