23

So I am reading this book titled Java Concurrency in Practice and I am stuck on this one explanation which I cannot seem to comprehend without an example. This is the quote:

When thread A writes to a volatile variable and subsequently thread B reads that same variable, the values of all variables that were visible to A prior to writing to the volatile variable become visible to B after reading the volatile variable.

Can someone give me a counterexample of why "the values of ALL variables that were visible to A prior to writing to the volatile variable become visible to B AFTER reading the volatile variable"?

I am confused why all other non-volatile variables do not become visible to B before reading the volatile variable?

Dmide
  • 6,422
  • 3
  • 24
  • 31
denniss
  • 17,229
  • 26
  • 92
  • 141
  • 1
    Java Concurrency in Practice is an awesome book. Every java programmer should read it! – Bohemian Jun 07 '11 at 01:25
  • 3
    to put it simply: the volatile semantics are ordering guarantees, so if anything happens before the operation to the volatile (read/write) it'd be as well read/written before operation, effectively following the same order of operations. Others the compilers AND CPUs are allowed to perform out-order read/writes and execution to significantly boost performance. – bestsss Jun 07 '11 at 07:37
  • Have a look sample and explanation here: http://stackoverflow.com/questions/10620680/why-volatile-in-java-5-doesnt-synchronize-cached-copies-of-variables-with-main – Grigory Kislin Oct 14 '13 at 08:28

5 Answers5

25

Declaring a volatile Java variable means:

  • The value of this variable will never be cached thread-locally: all reads and writes will go straight to "main memory".
  • Access to the variable acts as though it is enclosed in a synchronized block, synchronized on itself.

Just for your reference, When is volatile needed ?

When multiple threads using the same variable, each thread will have its own copy of the local cache for that variable. So, when it's updating the value, it is actually updated in the local cache not in the main variable memory. The other thread which is using the same variable doesn't know anything about the values changed by the another thread. To avoid this problem, if you declare a variable as volatile, then it will not be stored in the local cache. Whenever thread are updating the values, it is updated to the main memory. So, other threads can access the updated value.

From JLS §17.4.7 Well-Formed Executions

We only consider well-formed executions. An execution E = < P, A, po, so, W, V, sw, hb > is well formed if the following conditions are true:

  1. Each read sees a write to the same variable in the execution. All reads and writes of volatile variables are volatile actions. For all reads r in A, we have W(r) in A and W(r).v = r.v. The variable r.v is volatile if and only if r is a volatile read, and the variable w.v is volatile if and only if w is a volatile write.

  2. Happens-before order is a partial order. Happens-before order is given by the transitive closure of synchronizes-with edges and program order. It must be a valid partial order: reflexive, transitive and antisymmetric.

  3. The execution obeys intra-thread consistency. For each thread t, the actions performed by t in A are the same as would be generated by that thread in program-order in isolation, with each write wwriting the value V(w), given that each read r sees the value V(W(r)). Values seen by each read are determined by the memory model. The program order given must reflect the program order in which the actions would be performed according to the intra-thread semantics of P.

  4. The execution is happens-before consistent (§17.4.6).

  5. The execution obeys synchronization-order consistency. For all volatile reads r in A, it is not the case that either so(r, W(r)) or that there exists a write win A such that w.v = r.v and so(W(r), w) and so(w, r).

Useful Link : What do we really know about non-blocking concurrency in Java?

reevesy
  • 3,452
  • 1
  • 26
  • 23
Saurabh Gokhale
  • 53,625
  • 36
  • 139
  • 164
  • I am sorry but does this answer my last question to why all other non-vol vars are not visible to B before B reads the volatile variable? I truly appreciate your explanation though it is crystal clear! – denniss Jun 07 '11 at 01:33
  • @denniss I believe other sections of JCP explain that Java's Memory Model allows non-volatile variables to essentially be read out of order; the runtime is free to re-order the execution of statements where it doesn't affect program flow. – matt b Jun 07 '11 at 02:38
  • @denniss : Yes, absolutely. Reading volatile variable is like entering a synchronized block, writing to a volatile varible is like leaving the synchronized block. Hence all the non-vars are not visible to B before B reads volatile variable. – Saurabh Gokhale Jun 07 '11 at 04:11
  • @ 99tm: It's worth pointing out that there are differences between primitives and objects, because volatile is only about the object's reference and not the object itself: Therefore you can't declare an object as volatile and final altogether. However, declaring an object as final is much faster indeed, since the reference never changes: Therefore you must use final objects wherever you can. Finally, using volatile objects still needs synchronization - and/or volatile variables by themselves - when changing it, at least if you want to write consistent applications. – Marcus Jan 27 '14 at 14:13
  • +1. I attended an interview few days ago and I was asked about volatile and I told that threads will not cache the value but will pick the latest value from main memory. Then the question came where in main memory? I could not answer this? Can you please let me know where in main memory the volatile Objects or simple primitive variable of type int stored? – Amarnath Jan 21 '17 at 11:53
15

Thread B may have a CPU-local cache of those variables. A read of a volatile variable ensures that any intermediate cache flush from a previous write to the volatile is observed.

For an example, read the following link, which concludes with "Fixing Double-Checked Locking using Volatile":

http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

Brett Kail
  • 33,593
  • 2
  • 85
  • 90
  • 2
    so you are saying that when a thread reads a VOLATILE variable, it flushes the cache for all other variables as well? – denniss Jun 07 '11 at 01:34
  • 4
    Correct. Java defines volatile such that a volatile-read of a variable has the same memory effects as a synchronized-begin, and a volatile-write of the same variable has the same memory effects as a synchronized-end. In other words "if (volatile-read) { do-work; volatile-write }" is like a synchronized block that allows multiple threads to execute do-work at the same time. – Brett Kail Jun 07 '11 at 02:17
  • volatile reads don't flush the caches... (they just read w/o reordering) – bestsss Jun 07 '11 at 07:17
  • 1
    @denniss, no it doesn't flush any cache, the cache flushing is performed by *cache coherence protocol* and it's hardware level. Generally any writes to a cache line held by the processor will be updated (usually not flushed) by any write to the same cache line from another processor. Java memory model doesn't respect any hardware memory model precisely, most of the models are weaker. (some link http://www.infoq.com/presentations/click-crash-course-modern-hardware with an excellent video on java and hardware) – bestsss Jun 07 '11 at 07:30
  • so why does B can only see other NON-VOL variables after reading the VOL var? – denniss Jun 07 '11 at 07:35
  • 1
    @dennis, after write to the volatile variable thread A issues memory fence, hence everything written out be flushed. Since B reads in somehow reverse order first volatile, then rest, they are consistent. W/o the ordering rules, both threads are allowed to any out-of-order read/writes. This is what basically happens on the hardware level (I don't deviate more from java spec) but the statement that the caches are flushed doesesn't reflect reality. Such stuff would cause like 100 times performance degrade. – bestsss Jun 07 '11 at 07:57
  • @bestsss http://g.oswego.edu/dl/jmm/cookbook.html Look at the "Memory Barriers" section. In the table, a LoadLoad barrier must be emitted between a [1st operation = Volatile Load] and a [2nd operation = Normal Load]. (Also note from the table in the "Multiprocessors" section that LoadLoad stores are no-op on several CPUs.) Perhaps I inappropriately used the term "cache", but using a speculative load during OOO execution is effectively using a cached value, and I think it's accurate enough to think in terms of a "cache" when first learning about the JMM. – Brett Kail Jun 07 '11 at 14:43
  • @bkail but *LoadLoad* doesn't flush caches per se (actually I dont think it flushes ever). On ARM and IA-64 it's an order load (which I tried to tell). Real cache flushes like (CLFLUSH) are not used anywhere to my knowledge. The basic idea is not the cache is dumped (and registers are not used) but that the load operation is ordered with regard to the previous volatile load. – bestsss Jun 07 '11 at 15:03
  • @bkail other I try to stress is: volatile reads are cheap (can be as cheap as 1-3CPU cycles) and people should not be afraid to use volatile b/c it's expensive. The side effect of preventing some optimization still holds true but it's nowhere near entering a lock or L1 cache flush. – bestsss Jun 07 '11 at 15:07
  • @bestsss Ah, I see your point. I've updated the answer to emphasize that the volatile-write is flushing the cache, and the volatile-read simply ensures consistency. Does that make sense? – Brett Kail Jun 07 '11 at 16:53
6

If a variable is non-volatile, then the compiler and the CPU, may re-order instructions freely as they see fit, in order to optimize for performance.

If the variable is now declared volatile, then the compiler no longer attempts to optimize accesses (reads and writes) to that variable. It may however continue to optimize access for other variables.

At runtime, when a volatile variable is accessed, the JVM generates appropriate memory barrier instructions to the CPU. The memory barrier serves the same purpose - the CPU is also prevent from re-ordering instructions.

When a volatile variable is written to (by thread A), all writes to any other variable are completed (or will atleast appear to be) and made visible to A before the write to the volatile variable; this is often due to a memory-write barrier instruction. Likewise, any reads on other variables, will be completed (or will appear to be) before the read (by thread B); this is often due to a memory-read barrier instruction. This ordering of instructions that is enforced by the barrier(s), will mean that all writes visible to A, will be visible B. This however, does not mean that any re-ordering of instructions has not happened (the compiler may have performed re-ordering for other instructions); it simply means that if any writes visible to A have occurred, it would be visible to B. In simpler terms, it means that strict-program order is not maintained.

I will point to this writeup on Memory Barriers and JVM Concurrency, if you want to understand how the JVM issues memory barrier instructions, in finer detail.

Related questions

  1. What is a memory fence?
  2. What are some tricks that a processor does to optimize code?
Community
  • 1
  • 1
Vineet Reynolds
  • 76,006
  • 17
  • 150
  • 174
  • @Vinnet, there is no prob to read volatile variable from L1 cache, it's a task of the hardware to synchronize the caches of the CPU (update or flush the affected cache lines). Going to the mian RAM is awfully slow and such designs are bound to fail on performance. – bestsss Jun 07 '11 at 08:13
  • @Vinner just checked the linked article, it's a very good one. – bestsss Jun 07 '11 at 08:23
  • Is there any guarantee that a Java implementation will use semantics resembling "full" memory barriers? By my understanding, even if the JIT inserts a memory barrier instruction before a final-field store, for example, that would not prevent it from moving what the JVM spec would consider "unrelated" code across the barrier. I know of no means by which Java code can simply say "I want a memory here, and I don't want anything moved across it". In .NET a synchronized block would achieve that, but in Java it won't. – supercat Dec 03 '14 at 18:34
3

Threads are allowed to cache variable values that other threads may have since updated since they read them. The volatile keyword forces all threads to not cache values.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
1

This is simply an additional bonus the memory model gives you, if you work with volatile variables.

Normally (i.e. in the absence of volatile variables and synchronization), the VM can make variables from one thread visible to other threads in any order it wants, or not at all. E.g. the reading thread could read some mixture of earlier versions of another threads variable assignments. This is caused by the threads being maybe run on different CPUs with their own caches, which are only sometimes copied to the "main memory", and additionally by code reordering for optimization purposes.

If you used a volatile variable, as soon as thread B read some value X from it, the VM makes sure that anything which thread A has written before it wrote X is also visible to B. (And also everything which A got guaranteed as visible, transitively).

Similar guarantees are given for synchronized blocks and other types of locks.

Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210