Java volatile variables affecting memory consistency of other non-volatile variables - java

Scenario A
A1. Write to a volatile variable
A2. Flush all local non-volatile variable writes to main memory
Scenario B
B1. Read from a volatile variable
B2. Reload all non-volatile variables from main memory to local memory
Are scenarios A and B the correct behavior involved with volatile
variables? Or does Scenario A also include B2, or does Scenario B
also include A2?
Are these scenarios atomic? Can anything else happen
in between A1 and A2? Or B1 and B2?
(using Java 1.8 / 1.5+)

Writing to a volatile variables does not guarantee to flush non-volatile variables1. However, it will introduce a "happens before" relation between the write to the volatile and any subsequent read of the volatile (assuming no intervening writes to it). You can exploit this as follows:
Thread A : write NV
Thread A : write V
Thread B : read V
Thread B : read NV
If the actions happen in that order, then Thread B will see updated value of NV in step 4. However, if something (including A) writes to NV after step 2, it is unspecified what Thread B will see at step 4.
In general, using volatiles in this way requires deep and careful reasoning. It is easier and more robust to use synchronized.
Your example is unclear:
If it is intended to be a description of what the Java programmer must do, it is wrong / nonsensical. Java code cannot flush variables.
If it is intended to be a specification of what must happen at the implementation level (e.g. in the JIT compiled code), it is also wrong.
If it is intended to be a description of what could happen at the implementation level (e.g. in the JIT compiled code), it is correct.
I'm not just being pedantic here. The compiler may decide that it doesn't need to flush all local non-volatiles in Thread A, and it will most likely only reload the ones that it needs in Thread B. How it decides? That's the compiler writers' business!
1 - The JLS does not require hardware specific operations such as flushes. Instead, it requires the compiled code to meet some specific guarantees of memory visibility, and leaves the implementation to the compiler writer.

The actual rule is "A write to a volatile variable v (§8.3.1.4) synchronizes-with all subsequent reads of v by any thread (where "subsequent" is defined according to the synchronization order)." http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4
In other words, writes from one thread up until a write to v are all visible to reads from another thread once it has read v subsequently to that write.
I'm not certain that "flushing to main" is a necessary way to understand that. The Java memory model is documented in terms of happens-before and synchronizes-with. I recommend thinking of it in those terms. Conceptually a JVM could omit certain "flushes" if they aren't necessary to the promise.

Related

Does 'volatile' guarantee that any thread reads the most recently written value?

From the book Effective Java:
While the volatile modifier performs no mutual exclusion, it guarantees that any thread that reads the field will see the most recently written value
SO and many other sources claim similar things.
Is this true?
I mean really true, not a close-enough model, or true only on x86, or only in Oracle JVMs, or some definition of "most recently written" that's not the standard English interpretation...
Other sources (SO example) have said that volatile in Java is like acquire/release semantics in C++. Which I think do not offer the guarantee from the quote.
I found that in the JLS 17.4.4 it says "A write to a volatile variable v (§8.3.1.4) synchronizes-with all subsequent reads of v by any thread (where "subsequent" is defined according to the synchronization order)." But I don't quite understand.
There are quite some sources for and against this, so I'm hoping the answer is able to convince that many of those (on either side) are indeed wrong - for example reference or spec, or counter-example code.
Is this true?
I mean really true, not a close-enough model, or true only on x86, or only in Oracle JVMs, or some definition of "most recently written" that's not the standard English interpretation...
Yes, at least in the sense that a correct implementation of Java gives you this guarantee.
Unless you are using some exotic, experimental Java compiler/JVM (*), you can essentially take this as true.
From JLS 17.4.5:
A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.
(*) As Stephen C points out, such an exotic implementation that doesn't implement the memory model semantics described in the language spec can't usefully (or even legally) be described as "Java".
The quote per-se is correct in terms of what is tries to prove, but it is incorrect on a broader view.
It tries to make a distinction of sequential consistency and release/acquire semantics, at least in my understanding. The difference is rather "thin" between these two terms, but very important. I have tried to simplify the difference at the beginning of this answer or here.
The author is trying to say that volatile offers that sequential consistency, as implied by that:
"... it guarantees that any thread.."
If you look at the JLS, it has this sentence:
A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.
The tricky part there is that subsequent and it's meaning, and it has been discussed here. What is really wants to mean is "subsequent that observes that write". So happens-before is guaranteed when the reader observes the value that the writer has written.
This already implies that a write is not necessarily seen on the next read, and this can be the case where speculative execution is allowed. So in this regard, the quote is miss-leading.
The quote that you found:
A write to a volatile variable v (§8.3.1.4) synchronizes-with all subsequent reads of v by any thread (where "subsequent" is defined according to the synchronization order)
is a complicated to understand without a much broader context. In simple words, it established synchronizes-with order (and implicitly happens-before) between two threads, where volatile v variables is a shared variable. here is an answer where this has broader explanation and thus should make more sense.
It is not true. JMM is based on sequential consistency and for sequential consistency real time ordering isn't guaranteed; for that you need linearizability. In other words, reads and writes can be skewed as long as the program order isn't violated (or as long is it can't be proven po was violated).
A read of volatile variable a, needs to see the most recent written value before it in the memory order. But that doesn't imply real time ordering.
Good read about the topic:
https://concurrency-interest.altair.cs.oswego.narkive.com/G8KjyUtg/relativity-of-guarantees-provided-by-volatile.
I'll make it concrete:
Imagine there are 2 CPU's and (volatile) variable A with initial value 0. CPU1 does a store A=1 and CPU2 does a load of A. And both CPUs have the cacheline containing A in SHARED state.
The store is first speculatively executed and written to the store buffer; eventually the store commits and retires, but since the stored value is still in the store buffer; it isn't visible yet to the CPU2. Till so far it wasn't required for the cacheline to be in an EXCLUSIVE/MODIFIED state, so the cacheline on CPU2 still contains the old value and hence CPU2 can still read the old value.
So in the real time order, the write of A is ordered before the read of A=0, but in the synchronization order, the write of A=1 is ordered after the read of A=0.
Only when the store leaves the store buffer and wants to enter the L1 cache, the request for ownership (RFO) is send to all other CPU's which set the cacheline containing A to INVALID on CPU2 (RFO prefetching I'll leave out of the discussion). If CPU2 would now read A, it is guaranteed to see A=1 (the request will block till CPU1 has completed the store to the L1 cache).
On acknowledgement of the RFO the cacheline is set to MODIFIED on CPU1 and the store is written to the L1 cache.
So there is a period of time between when the store is executed/retired and when it is visible to another CPU. But the only way to determine this is if you would add special measuring equipment to the CPUs.
I believe a similar delaying effect can happen on the reading side with invalidation queues.
In practice this will not be an issue because store buffers have a limited capacity and need to be drained eventually (so a write can't be invisible indefinitely). So in day to day usage you could say that a volatile read, reads the most recent write.
A java volatile write/read provides release/acquire semantics, but keep in mind that the volatile write/read is stronger than release/acquire semantics. A volatile write/read is sequential consistent and release/acquire semantics isn't.

How does the JVM guarantee the visibility of member variable modifications in the referenced object when using synchronized?

I want to know how does the JVM guarantee the visibility of member variable modifications in the referenced object when using synchronized.
I know synchronized and volatile will provide visibility for variable modifications.
class Test{
public int a=0;
public void modify(){
a+=1;
}
}
//Example:
// Thread A:
volatile Test test=new Test();
synchronized(locker){
test.modify();
}
// then thread B:
synchronized(locker){
test.modify();
}
// Now, I think test.a==2 is true. Is it ok? How JVM implements it?
// I know the memory barrier, does it flush all cache to main storage?
Thread A call modify in a sychronized block first, and then pass the object to thread B (Write the reference to a volatile variable.).
Then thread B call modify again (in synchronized).
Is there any guarantee for a==2? And how is the JVM implemented?
Visibility between threads is enforced with Memory Barriers/Fences. In case of synchronized block JVM will insert a memory barrier after the execution of the block completes.
JVM implements memory barriers with CPU instruction e.g. a store barrier is done with sfence and load barrier is done with lfence instruction on x86. There is also mfence and possibly other instructions which can be specific to CPU architecture.
For your (still incomplete!) example, if we can assume the following:
The code in thread A initializing test is guaranteed to run before thread B uses it.
The locker variable contains a reference to the same object for threads A & B.
then we can prove that a == 2 will be true at the point you indicate. If precondition 1 is not guaranteed, then thread B may get an NPE. If precondition 2 is not guaranteed (i.e. threads A and B may synchronize on different objects) then there is not a proper happens-before relationship to ensure that thread B sees the result of thread A's actions on a.
(#NathanHughes commented that the volatile is unnecessary. I wouldn't necessarily agree with that. It depends on details of your example that you still haven't show us.)
How JVM implements it?
The actual implementation is Java platform and (in theory) version specific. The JVM spec Memory Model places constraints on how a program that obeys "the rules" will behave. It is entirely implementation specific how that actually happens.
I know the memory barrier, does it flush all cache to main storage?
That is implementation specific too. There are different kinds of memory barrier that work in different ways. The JIT compiler will emit native code that uses the appropriate instructions to meet the guarantees required by the JLS. If there is a way to do this without doing a full cache flush then the implementation may do that.
(There is a JVM command line option to tell the JIT compiler to output the native code. If you really want to know what is happening under the hood, that is a good place to start looking.)
But if you are trying to understand / analyze your application's thread-safety, you should be doing it in terms of the Java Memory Model. Also, use higher level concurrency abstractions that allow you to avoid the lower level pitfalls.

Java volatile reordering prevention scope

Writes and reads to a volatile field prevent reordering of reads/writes before and after the volatile field respectively. Variable reads/writes before a write to a volatile variable can not be reordered to happen after it, and reads/writes after a read from a volatile variable can not be reordered to happen before it. But what is the scope of this prohibition? As I understand volatile variable prevents reordering only inside the block where it is used, am I right?
Let me give a concrete example for clarity. Let's say we have such code:
int i,j,k;
volatile int l;
boolean flag = true;
void someMethod() {
int i = 1;
if (flag) {
j = 2;
}
if (flag) {
k = 3;
l = 4;
}
}
Obviously, write to l will prevent write to k from reordering, but will it prevent reordering of writes to i and j in respect to l? In other words can writes to i and j happen after write to l?
UPDATE 1
Thanks guys for taking your time and answering my question - I appreciate this. The problem is you're answering the wrong question. My question is about scope, not about the basic concept. The question is basically how far in code does complier guarantee the "happens before" relation to the volatile field.
Obviously compiler can guarantee that inside the same code block, but what about enclosing blocks and peer blocks - that's what my question is about. #Stephen C said, that volatile guarantees happen before behavior inside the whole method's body, even in the enclosing block, but I can not find any confirmation to that. Is he right, is there a confirmation somewhere?
Let me give yet another concrete example about scoping to clarify things:
setVolatile() {
l = 5;
}
callTheSet() {
i = 6;
setVolatile();
}
Will compiler prohibit reordering of i write in this case? Or maybe compiler can not/is not programmed to track what happens in other methods in case of volatile, and i write can be reordered to happen before setVolatile()? Or maybe compiler doesn't reorder method calls at all?
I mean there is got to be a point somewhere, when compiler will not be able to track if some code should happen before some volatile field write. Otherwise one volatile field write/read might affect ordering of half of a program, if not more. This is a rare case, but it is possible.
Moreover, look at this quote
Under the new memory model, it is still true that volatile variables cannot be reordered with each other. The difference is that it is now no longer so easy to reorder normal field accesses around them.
"Around them". This phrase implies, that there is a scope where volatile field can prevent reordering.
Obviously, write to l will prevent write to k from reordering, but will it prevent reordering of writes to i and j?
It is not entirely clear what you mean by reordering; see my comments above.
However, in the Java 5+ memory model, we can say that the writes to i and j that happened before the write to l will be visible to another thread after it has read l ... provided that nothing writes i and j after write to l.
This does have the effect of constraining any reordering of the instructions that write to i and j. Specifically, they can't be moved to after the memory write barrier following the write to l, because that could lead them to not being visible to the second thread.
But what is the scope of this prohibition?
There isn't a prohibition per se.
You need to understand that instructions, reordering and memory barriers are just details of a specific way of implementing the Java memory model. The model is actually defined in terms of what is guaranteed to be visible in any "well-formed execution".
As I understand volatile prevents reordering inside the block where it is used, am I right?
Actually, no. The blocks don't come into the consideration. What matters is the (program source code) order of the statements within the method.
#Stephen C said, that volatile guarantees happen before behavior inside the whole method's body, even in the enclosing block, but I can not find any confirmation to that.
The confirmation is JLS 17.4.3. It states the following:
Among all the inter-thread actions performed by each thread t, the program order of t is a total order that reflects the order in which these actions would be performed according to the intra-thread semantics of t.
A set of actions is sequentially consistent if all actions occur in a total order (the execution order) that is consistent with program order, and furthermore, each read r of a variable v sees the value written by the write w to v such that:
w comes before r in the execution order, and
there is no other write w' such that w comes before w' and w' comes before r in the execution order.
Sequential consistency is a very strong guarantee that is made about visibility and ordering in an execution of a program. Within a sequentially consistent execution, there is a total order over all individual actions (such as reads and writes) which is consistent with the order of the program, and each individual action is atomic and is immediately visible to every thread.
If a program has no data races, then all executions of the program will appear to be sequentially consistent.
Notice that there is NO mention of blocks or scopes in this definition.
EDIT 2
The volatile ONLY gaurentee the happens-before relation.
Why it reorder in single thread
Considered we have two fields:
int i = 0;
int j = 0;
We have a method to write them
void write() {
i = 1;
j = 2;
}
As you know, compiler may reorder them. That is because compiler think it is not matter access which first. Because in single thread, they are 'happen together'.
Why can't reorder in multi thread
But now we have another method to read them in another thread:
void read() {
if(j==2) {
assert i==1;
}
}
If compiler still reorder it, this assert may fail. That means j has been 2, but i unexpectly is not 1. Which seems i=1 is happens after assert i==1.
What volatile do
The volatile only gaurantee the happens-before relation.
Now we add volatile
volatile int j = 0;
When we observe j==2 is true, that means j=2 is happened and i=2 is before it, it must happened. So the assert will never fail now.
'Prventing reorder' is just an approach that compiler to provide that guarantee.
Conclusion
The only things you should now is happens-before. Please refer to the link below of java specification. The reordering or not is just a side effect of this guarantee.
Answer for you question
Since l is volatile, acccess to i and j always before access to l in the someMethod. The fact is, every thing before the line l=4 will happen before before it.
EDIT 1
Since the post has been edit. Here is further explasion.
A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.
happens-before means:
If one action happens-before another, then the first is visible to and ordered before the second.
So the access to i and j happen-before access to l.
reference: https://docs.oracle.com/javase/specs/jls/se10/html/jls-17.html#jls-17.4.5
Origin answer
No, the volatile only protect itself, though it is not easy to reorder field access near volatile.
Under the new memory model, it is still true that volatile variables cannot be reordered with each other. The difference is that it is now no longer so easy to reorder normal field accesses around them. Writing to a volatile field has the same memory effect as a monitor release, and reading from a volatile field has the same memory effect as a monitor acquire. In effect, because the new memory model places stricter constraints on reordering of volatile field accesses with other field accesses, volatile or not, anything that was visible to thread A when it writes to volatile field f becomes visible to thread B when it reads f.
The volatile keyword only guarantee that:
A write to a volatile field happens before every subsequent read of that same volatile.
reference: http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile
I am curious to know how volatile variable affects OTHER fields
Volatile variables do affect the other fields. JIT compiler can reorder the instructions if he thinks that reordering will not have any impact on the execution output. So if you have 6 independent variable stores JIT can reorder the instructions.
However if you make a variable volatile i.e. in your case variable l then JIT will not reorder any variable STORES after the volatile STORE. And I think that makes sense because in a multithreaded program if I get the value of variable l as 4, then I should get i as 1, because in my program i was written before l and which eventually is Program Order Semantics (If I am not wrong).
Note that volatile variables does two things:
Compiler will not reorder any stores after volatile store / not reorder any reads before volatile read.
Flushes the Load/Store buffer so that all the processor can see the changes.
EDIT:
Good blog here: http://jpbempel.blogspot.com/2013/05/volatile-and-memory-barriers.html
Maybe I know the "real scope" you are in dout.
Two types of reorder is the main reason of unordering instruction result:
1. Compiler optimization
2. Cpu processor recordering(maily caused by cache and main memory synchronize)
volatile keyword first need to confirm the flushing of volatile variable, at the meantime, other variables are also flushed to main memory.But because of compiler reordering, some writable instructions before the volatile valatile variable may be reordered after the volatile variable, the reader may be confused to read not the real time other variable values which is before the volatile variable in program order, so the rule of "variables writting instruction before the volatile variable is forced to run before the volatile" is made.This optimazation is done by Java Compiler or JIT.
The main point is optimization of compiler in instructions,like finding dead code , instruction reorder operation, the instructions code range is always a "basic block"(Except some other constant propagation optimization, etc.). A basic block is an set of instructions without jmp instruction inside, so this is a basic block. So in my opinion, the reorder operation is fixed in the range basic block.
the basic block in source code is always a block or the body of a method.
And also because java does not have inline function, the method call is used by dynamic invoke method instruction, the reorder operation should not be across two method.
So, the scope will not be larger than a "method body", or maybe only a area of "for" body , it's the basic block range.
This is all my thought, I'm not sure if it is right, someone can help to make it more accurate.

Java volatile effect on other variables [duplicate]

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?
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:
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.
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.
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.
The execution is happens-before consistent
(§17.4.6).
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?
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
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
What is a memory fence?
What are some tricks that a processor does to optimize code?
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.
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.

Does volatile influence non-volatile variables?

Okay, suppose I have a bunch of variables, one of them declared volatile:
int a;
int b;
int c;
volatile int v;
If one thread writes to all four variables (writing to v last), and another thread reads from all four variables (reading from v first), does that second thread see the values written to a, b and c by the first thread, even though they are not themselves declared volatile? Or can it possibly see stale values?
Since there seems to be some confusion: I'm not deliberately trying to do something unsafe. I just want to understand the Java memory model and the semantics of the volatile keyword. Pure curiosity.
I'm going to speak to what I think you may really be probing about—piggybacking synchronization.
The technique that it looks like you're trying to use involves using one volatile variable as a synchronization guard in concert with one or more other non-volatile variables. This technique is applicable when the following conditions hold true:
Only one thread will write to the set of values meant to be guarded.
The threads reading the set of values will read them only if the volatile guard value meets some criteria.
You don't mention the second condition holding true for your example, but we can examine it anyway. The model for the writer is as follows:
Write to all the non-volatile variables, assuming that no other thread will try to read them.
Once complete, write a value to the volatile guard variable that indicates that the readers' criteria is met.
The readers operate as follows:
Read the volatile guard variable at any time, and if its value meets the criteria, then
Read the other non-volatile variables.
The readers must not read the other non-volatile variables if the volatile guard variable does not yet indicate a proper value.
The guard variable is acting as a gate. It's closed until the writer sets it to a particular value, or set of values that all meet the criteria of indicating that the gate is now open. The non-volatile variables are guarded behind the gate. The reader is not permitted to read them until the gate opens. Once the gate is open, the reader will see a consistent view of the set of non-volatile variables.
Note that it is not safe to run this protocol repeatedly. The writer can't keep changing the non-volatile variables once it's opened the gate. At that point, multiple reader threads may be reading those other variables, and they can—though are not guaranteed—see updates to those variables. Seeing some but not all of those updates would yield inconsistent views of the set.
Backing up, the trick here is to control access to a set of variables without either
creating a structure to hold them all, to which an atomic reference could be swapped, um, atomically, or
using a lock to make writing to and reading from the entire set of variables mutually exclusive activities.
Piggybacking on top of the volatile guard variable is a clever stunt—not one to be done casually. Subsequent updates to the program can break the aforementioned fragile conditions, removing the consistency guarantees afforded by the Java memory model. Should you choose to use this technique, document its invariants and requirements in the code clearly.
Yes. volatile, locks, etc., setup the happens-before relationship, but it affects all variables (in the new Java Memory Model (JMM) from Java SE 5/JDK 1.4). Kind of makes it useful for non-primitive volatiles...
does that second thread see the values written to a, b and c by the first thread, even though they are not themselves declared volatile? Or can it possibly see stale values?
You will get stale reads, b/c you can't ensure that the values of a, b, c are the ones set after reading of v. Using state machine (but you need CAS to change the state) is a way to tackle similar issues but it's beyond the scope of the discussion.
Perhaps this part is unclear, after writing to v and reading first from v, you'd get the right results (non-stale reads), the main issue is that if you do
if (v==STATE1){...proceed...}, there is no guarantee some other thread would not be modifying the state of a/b/c. In that case, there will be state reads.
If you modify the a/b/c+v once only you'd get the correct result.
Mastering concurrency and and lock-free structures is a really hard one. Doug Lea has a good book on and most talks/articles of Dr. Cliff Click are a wonderful wealth, if you need something to start digging in.
Yes, volatile write "happens-before" next volatile read on the same variable.
While #seh is right on about consistency problems with multiple variables, there are use cases that less consistency is required.
For example, a writer thread updates some state variables; a reader thread displays them promptly. There's not much relation among the variables, we only care about reading the new values promptly. We could make every state variable volatile. Or we could use only one volatile variable as visibility guard.
However, the saving is only on the paper, performance wise there's hardly any difference. In either version, every state variable must be "flushed" by the writer and "loaded" by the reader. No free lunch.

Categories