I\'m trying to illustrate the use and importance of volatile
with an example that would really not give a good result if volatile
was omitted.
Simplifying @Elf example further, where the other thread will never get the value which was updated by other thread. Removing System.out.println as there is synchronized code inside println and out is static, somehow that helps the other thread to get the latest value of flag variable.
public class VolatileExample implements Runnable {
public static boolean flag = true;
public void run() {
while (flag);
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new VolatileExample());
thread.start();
Thread.sleep(1000L);
flag = false;
thread.join();
}
}
UPDATE My answer is wrong, see answer from irreputable.
It's not thread-safe, since access to there's only one writer thread. Should there be another writer thread, value of count
is notcount
would be become inconsistent to the number of updates.
Visibility of count
value to main thread is ensured by checking stopped
volatile inside getCount
method. This is what is called piggybacking on synchronization
in Concurrency in practice
book.
Wrong code with which we cannot assume x = 1 also if y is already 2:
Class Reordering {
int x = 0, y = 0;
public void writer() {
x = 1;
y = 2;
}
public void reader() {
int r1 = y;
int r2 = x;
}
}
Example of use of volatile keyword:
class VolatileExample {
int x = 0;
volatile boolean v = false;
public void writer() {
x = 42;
v = true;
}
public void reader() {
if (v == true) {
//uses x - guaranteed to see 42.
}
}
}
Source: http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html
It was always hard to me to illustrate concurrency problems in a convincing way: well, fine, it's all nice about happens-before and stuff, but why should one care? Is there a real problem? There are lots and lots of poorly written, poorly synchronized programs—and they still work most of the time.
I used to find a resort in a "works most of the time VS works" rhetoric—but, frankly, it's a weak approach. So what I needed is an example which would make difference obvious—and, preferably, painful.
So here is a version which actually does show the difference:
public class VolatileExample implements Runnable {
public static boolean flag = true; // do not try this at home
public void run() {
long i = 0;
while (flag) {
if (i++ % 10000000000L == 0)
System.out.println("Waiting " + System.currentTimeMillis());
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new VolatileExample());
thread.start();
Thread.sleep(10000L);
flag = false;
long start = System.currentTimeMillis();
System.out.println("stopping " + start);
thread.join();
long end = System.currentTimeMillis();
System.out.println("stopped " + end);
System.out.println("Delay: " + ((end - start) / 1000L));
}
}
A simple run shows:
Waiting 1319229217263
stopping 1319229227263
Waiting 1319229242728
stopped 1319229242728
Delay: 15
That is, it takes more than ten seconds (15 here) for a running thread to notice there was any change.
With volatile
, you have:
Waiting 1319229288280
stopping 1319229298281
stopped 1319229298281
Delay: 0
that is, exiting (almost) immediately. The resolution of currentTimeMillis
is around 10ms, so the difference is more that 1000 times.
Note it was Apple's version of (ex-)Sun JDK, with -server
option. The 10-second wait was added in order to let JIT compiler find out that the loop is hot enough, and optimize it.
Hope that helps.
Victor is right, there are issues with your code: atomicity and visibility.
Here's my edition:
private int count;
private volatile boolean stop;
private volatile boolean stopped;
@Override
public void run() {
while (!stop) {
count++; // the work
}
stopped = true;
System.out.println("Count 1 = " + count);
}
public void stopCounting() {
stop = true;
while(!stopped)
; //busy wait; ok in this example
}
public int getCount() {
if (!stopped) {
throw new IllegalStateException("not stopped yet.");
}
return count;
}
}
If a thread observes that stopped==true
, it's guaranteed that the work completes and the result is visible.
There is a happens-before relation from volatile write to volatile read (on the same variable), so if there are two threads
thread 1 thread 2
action A
|
volatile write
\
volatile read
|
action B
action A happens-before action B; writes in A are visible by B.
To illustrate the importance of the volatile
keyword when it comes to concurrency, all you need to do is make sure that the volatile
field is modified and read in a separate threads.