Ideally, if keepRunning wasn't volatile, thread should keep on running indefinitely. But, it does stop after few seconds.
If you are running in a single-processor or if your system is very busy, the OS may be swapping out the threads which causes some levels of cache invalidation. Not having a volatile
doesn't mean that memory will not be shared, but the JVM is trying to not synchronize memory if it can for performance reasons so the memory may not be updated.
Another thing to note is that System.out.println(...)
is synchronized because the underlying PrintStream
does synchronization to stop overlapping output. So you are getting memory synchronization "for free" in the main-thread. This still doesn't explain why the reading loop sees the updates at all however.
Whether the println(...)
lines are in or out, your program spins for me under Java6 on a MacBook Pro with an Intel i7.
Can anyone explain volatile with example ? Not with theory from JLS.
I think your example is good. Not sure why it isn't working with all System.out.println(...)
statements removed. It works for me.
Is volatile substitute for synchronization ? Does it achieve atomicity ?
In terms of memory synchronization, volatile
throws up the same memory barriers as a synchronized
block except that the volatile
barrier is uni-directional versus bi-directional. volatile
reads throw up a load-barrier while writes throw up a store-barrier. A synchronized
block is a bi-directional barrier with the addition of mutex locking.
In terms of atomicity
, however, the answer is "it depends". If you are reading or writing a value from a field then volatile
provides proper atomicity. However, incrementing a volatile
field suffers from the limitation that ++
is actually 3 operations: read, increment, write. In that case or more complex mutex cases, a full synchronized
block may be necessary. AtomicInteger
solves the ++
issue with a complicated test-and-set spin-loop.