Threading stories: Why volatile matters
Join the DZone community and get the full member experience.
Join For FreeMany 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
Opinions expressed by DZone contributors are their own.
Comments