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).
Unfortunately, you're missing an important detail: The TimerTask is running on a private background thread! Therefore, in your first example, the synchronized block has no effect, because it is not executed by the private timer thread.
ReplyDeleteThe second example has another problem: The private timer thread does not access a volatile field and does not execute a synchronized block. Therefore, it can cache the value of the field when writing to it. It is only because the run() method returns immediately that the field is forced to update and so the program accidentally works.
Declaring the field volatile
@Anonymous
ReplyDeleteHere is what I think (~know): The synchronized block only takes affect on the worker thread. The program has no issues with updating the expired variable. Instead, it has an issue with re-reading the updated value within the worker thread. The timer thread cancels and dies, the memory is updated and the worker thread cannot read the variable. If you put the synchronized block around the read of the expired variable then the worker thread must read from main memory and the whole thing works.
Cheers, Niklas
Hi Niklas,
ReplyDeleteNice article, but I am missing one point that is if in the example above that is the first one you have the expired variable as volatile and it is in sycnrhonized block so thread will always read from heap memory, please correct me if i am wrong.
The first example is OK. Only if expired variable would not be volatile then the thread would not be forced to reread from memory *although* everything is in a synchronized block. Synchronized only forces a thread to read a variable *once* from the heap. Volatile forces a thread to always fetch the value from the heap.
ReplyDelete