Threading stories: volatile and synchronized
Join the DZone community and get the full member experience.
Join For FreeIn my last blog about the volatile modifier I have introduced a little program that illustrates the behaviour of volatile
in a Java 6 (26) Hotspot VM. Since that day I had some interesting
discussions that I wanted to share in this blog. It adds another
valuable insights on the volatile modifier.
Here is my little program, which I have adopted a
little to make it easier. My previous example was originally thought as a
thread contention example, which will be the topic in one of my
upcoming posts.
import java.util.Timer; import java.util.TimerTask; public class AnotherVolatileExampleA { private volatile boolean expired = false; private long counter = 0; private Object mutex = new Object(); private class Worker implements Runnable { @Override public void run() { synchronized (mutex) { 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 ..."); }; } } public static void main(String[] args) throws InterruptedException { AnotherVolatileExampleA volatileExample = new AnotherVolatileExampleA(); Thread thread1 = new Thread(volatileExample.new Worker(), "Worker-1"); thread1.start(); } }
Now, this program still behaves similar like the one of my last blog. With volatile in line 6 the result written to the console is:
Timer interrupted main thread ... Main thread was interrupted by timer ...
Without volatile in line 6 the result is:
Timer interrupted main thread ...
One question in a discussion was, why that happens although everything takes place in a synchronized block. The Java VM specification says the synchronized keywork garantees that (less formal!) a variable is written into the memory heap and is read from the memory heap (read here). Now, this is true, but it's missing the point that the thread only needs to read the variable ONCE within a single synchronized
block. In the example above the expired variable is read once at the
very first while loop. Afterwards the thread does not need to read the
variable again. Consider this program:
import java.util.Timer; import java.util.TimerTask; public class AnotherVolatileExampleB { private boolean expired = false; private long counter = 0; private Object mutex = new Object(); private class Worker implements Runnable { @Override public void run() { final Timer timer = new Timer(); timer.schedule(new TimerTask() { public void run() { expired = true; System.out.println("Timer interrupted main thread ..."); timer.cancel(); } }, 1000); boolean tmpExpired = false; while (!tmpExpired) { synchronized (mutex) { tmpExpired = expired; } counter++; // do some work } System.out.println("Main thread was interrupted by timer ..."); } } public static void main(String[] args) throws InterruptedException { AnotherVolatileExampleB volatileExample = new AnotherVolatileExampleB(); Thread thread1 = new Thread(volatileExample.new Worker(), "Worker-1"); thread1.start(); } }
In that case the synchronized block is within the while loop (lines 23-25) and the thread is now forced to re-read the expired variable from the main memory in each loop 'cause synchronized garantees to read from memory once (same applies to Java 5 locks). The result of that program will be as expected from a synchronized block:
Timer interrupted main thread ... Main thread was interrupted by timer ...
Therefore, if you wish to read a variable from memory in a synchronized block (or within a Java 5 lock), remember that the thread only garantees to read the variable once from the memory heap. The volatile modifier, on the other hand, always garantees a "memory heap read" (see here).
From http://niklasschlimm.blogspot.com/2011/10/threading-stories-volatile-and.html
Opinions expressed by DZone contributors are their own.
Comments