Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Threading stories: Why volatile matters

DZone's Guide to

Threading stories: Why volatile matters

· Java Zone
Free Resource

Learn how to troubleshoot and diagnose some of the most common performance issues in Java today. Brought to you in partnership with AppDynamics.

Many years ago when I learned Java (in 2000) I was not so concerned about multithreading. In particular I wasn't concerned about the volatile modifier. I don't know why, but I never had problems without volatile, so maybe I thought it could not be so relevant. I've suddenly changed my mind when I first analyzed a wierd behaviour of an application that only existed when the application was deployed to a server JVM. Todays JVMs make a lot magic stuff to optimize runtime performance on server applications. In this blog I show you an example to get fimiliar with problems that arrize in multithreaded applications, when you don't recognize the importance of understanding how Java treats shared data in multithreaded programs.

This code snippet demonstrates why understanding volatile is important. Here is the code that you can use to play around. Notice in line 8 the expired variable is declared volatile:

import java.util.Timer;
import java.util.TimerTask;

public class VolatileExample {

 private volatile boolean expired;
 private long counter = 0;
 private Object mutext = new Object();

 @Override
 public Object[] execute(Object... arguments) {
  synchronized (mutext) {
   expired = false;
   final Timer timer = new Timer();
   timer.schedule(new TimerTask() {
    public void run() {
     expired = true;
     System.out.println("Timer interrupted main thread ...");
     timer.cancel();
    }
   }, 1000);
   while (!expired) {
    counter++; // do some work
   }
   System.out.println("Main thread was interrupted by timer ...");
  };
  return new Object[] { counter, expired };
 }

 private class Worker implements Runnable {
  @Override
  public void run() {
   while (!Thread.currentThread().isInterrupted()) {
    execute();
   }
  }
 }

 @SuppressWarnings("static-access")
 public static void main(String[] args) throws InterruptedException {
  VolatileExample volatileExample = new VolatileExample();
  Thread thread1 = new Thread(volatileExample.new Worker(), "Worker-1");
  Thread thread2 = new Thread(volatileExample.new Worker(), "Worker-2");
  thread1.start();
  thread2.start();
  Thread.currentThread().sleep(60000);
  thread1.interrupt();
  thread2.interrupt();
 }
}

 

Start that with Hotspot VM with -server option set. What you'll get is the following expected output:

Timer interrupted main thread ...
Main thread was interrupted by timer ...
Timer interrupted main thread ...
Main thread was interrupted by timer ...
Timer interrupted main thread ...
Main thread was interrupted by timer ...
Timer interrupted main thread ...
Main thread was interrupted by timer ...
Timer interrupted main thread ...
Main thread was interrupted by timer ...

 

Now take out the volatile in line 8 above and restart, again with -server option set. What you should get is the following output:

Timer interrupted main thread ...

What happened? The Timer thread sets the expired flag to true but the main thread does not see the change. This is exactly what volatile is all about: it ensures that threads share the actual value of a specific variable. If you declare a variable as volatile all threads read that specific value from the memory heap. In the described example the timer thread set the expired value within the thread and this update was not reflected in the memory heap! Notice, that I cancel the timer thread when I set the expired variable to true. This causes the timer thread to die immediately after the run()-method is passed. However, it does not make an effort to update the memory heap before it terminates. The change to the variable is lost.

Next: now restart the code again without the volatile modifier. This time you set the -client JVM option (which is the default mode on Windows). The result is the following:

Timer interrupted main thread ...
Main thread was interrupted by timer ...
Timer interrupted main thread ...
Main thread was interrupted by timer ...
Timer interrupted main thread ...
Main thread was interrupted by timer ...
Timer interrupted main thread ...
Main thread was interrupted by timer ...
Timer interrupted main thread ...
Main thread was interrupted by timer ...

 

In the client mode the JVM obviously behaves different and does not optimize so aggressively like in server mode. So even if you missed out the volatile modifier, you may not necessarily see an error during development. The JVM options influence the way how strict the JVM optimzes your code. Without volatile it is not garanteed that data changes made by the timer thread are visible to the main thread. But in this case for instance everything still works OK in client mode, which shows that the result of your program relies on the JVM options set. 

From http://niklasschlimm.blogspot.com/2011/10/threading-stories-why-volatile-matters.html

Understand the needs and benefits around implementing the right monitoring solution for a growing containerized market. Brought to you in partnership with AppDynamics.

Topics:

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}