Close

Java Memory Model - Visibility problem, fixing with volatile variable

[Last Updated: Mar 12, 2019]

When variable reads and writes occur in different threads, the reader thread cannot always see the changes made by writer thread immediately. That is due to the underlying processor architecture as described in the last tutorial. In general, there is no guarantee that the reader thread will see a value written by another thread on a timely basis, or even at all.
According to Java Memory Model, we must either establish some sort of happen-before relation between read and write threads or use synchronized blocks.


Example

In this example one thread increases a counter of type int and other thread attempts to read it:

public class ReaderWriterShareDataProblem {
    static int c;

    public static void main (String[] args) {

        Thread thread1 = new Thread(() -> {
            int temp = 0;
            while (true) {
                if (temp != c) {
                    temp = c;
                    System.out.println("reader: value of c = " + c);
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                c++;
                System.out.println("writer: changed value to = " + c);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            // sleep enough time to allow reader thread to read pending changes (if it can!).
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //exit the program otherwise other threads will be keep waiting on c to change.
            System.exit(0);
        });

        thread1.start();
        thread2.start();
    }
}

Output:

    writer: changed value to = 1
    reader: value of c = 1
    writer: changed value to = 2
    writer: changed value to = 3
    writer: changed value to = 4
    writer: changed value to = 5

    Process finished with exit code 0

As seen in the output the reader thread is not able to read the changes made with c at all, except for change in the default value, from 0 to 1.

According to JSR-133:

The write of the default value (zero, false or null) to each variable synchronizes-with to the first action in every thread.

Although it may seem a little strange to write a default value to a variable before the object containing the variable is allocated, conceptually every object is created at the start of the program with its default initialized values. Consequently, the default initialization of any object happens-before any other actions (other than default writes) of a program.


Note that output might vary for different kind of processor.

The machine I'm using:
    OS Name	Microsoft Windows 10 Home
    Version	10.0.10586 Build 10586
    System Model	GP72 2QE
    System Type	x64-based PC
    Processor	Intel(R) Core(TM) i7-5700HQ CPU @ 2.70GHz, 2701 Mhz, 4 Core(s), 8 Logical Processor(s)
    Installed Physical Memory (RAM)	16.0 GB


Fixing visibility problem with volatile variable

Using volatile forces all accesses (read or write) to occur to the main memory, effectively not caching volatile in CPU. This can be useful for the actions where visibility of the variable is important and order of accesses is not important.

In above example just replace

static int c;
with
static int volatile c;

New class:

public class ReaderWriterShareDataProblemVolatileFix {
    static volatile int c ;
 // same code as before
    .......
}

Output:

    writer: changed value to = 1
    reader: value of c = 1
    writer: changed value to = 2
    reader: value of c = 2
    writer: changed value to = 3
    reader: value of c = 3
    reader: value of c = 4
    writer: changed value to = 4
    reader: value of c = 5
    writer: changed value to = 5

    Process finished with exit code 0

As we can see in the output now, the changes in the value of c is visible to the reader thread immediately.

Using volatile vs synchronized blocks

Above scenario is related to read and write threads where threads don't overlap same piece of code but still cause data-race (please see last tutorial for a formal definition of data-race). In scenarios where threads interleave/overlap and try to read/write shared variables on the 'same method/piece of code' where atomicity of multiple actions is required, or perform some composite actions like ++, using 'volatile' will not be appropriate choice.
In those kind of cases we should use intrinsic locks on the shared code instead of volatile variables (in next tutorial we will see an example on that).


Why shouldn't we always use locks?

It's a common question that why bother about deciding in what scenarios we should use volatile or use intrinsic lock, why shouldn't we always use intrinsic locks. Using volatile (at appropriate scenarios) is important because the programmer is able to write cleaner and less error prone code as compare to writing synchronized blocks. It's kind of giving the synchronization responsibility to the JVM rather than doing it ourselves. Also depending on the runtime JVM implementation, volatile can have better throughput and better latency. However, if volatile fields are accessed frequently inside a single method, their use is likely to lead to slower performance than would locking the entire method.



Swing example

It's better to visualize the effect of using volatile in a Swing GUI. Let's modify our above example:

import javax.swing.*;
import java.awt.*;

public class ReaderWriterSwingWithVolatileExample {
    static volatile int c;

    public static void main (String[] args) {
        JFrame frame = createFrame();
        frame.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 5));

        final ProgressUi writerProgressUi = new ProgressUi("Writer Thread");
        final ProgressUi readerProgressUi = new ProgressUi("Reader Thread");

        frame.add(writerProgressUi.getProgressComponent());
        frame.add(readerProgressUi.getProgressComponent());

        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        Thread thread1 = new Thread(() -> {
            int temp = 0;
            while (true) {
                if (temp != c) {
                    temp = c;
                    readerProgressUi.update(temp);
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                c++;
                writerProgressUi.update(c);
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                if (i == 100) {
                    i = 1;
                    c = 0;
                }
            }
        });

        thread1.start();
        thread2.start();
    }

    private static class ProgressUi {
        private JProgressBar progressBar = new JProgressBar();

        ProgressUi (String name) {
            progressBar.setString(name);
            progressBar.setStringPainted(true);
        }

        JComponent getProgressComponent () {
            return progressBar;
        }

        void update (int c) {
            progressBar.setValue(c);
        }
    }

    private static JFrame createFrame () {
        JFrame frame = new JFrame("Visibility Demo with Volatile");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(new Dimension(400, 80));
        return frame;
    }
}


Using variable c with volatile keyword or without it, will give this output:



Example Project

Dependencies and Technologies Used:

  • JDK 1.8
  • Maven 3.0.4

Java - volatile Example Select All Download
  • shared-data-visibility-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • ReaderWriterShareDataProblem.java

    See Also