Reputation: 8672
I have found in this site some interesting questions (e.g. this one) about the visibility effects of volatile variables in Java originated from this paragraph taken from the book Java concurrency in Practice:
The visibility effects of volatile variables extend beyond the value of the volatile variable itself. 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. So from a memory visibility perspective, writing a volatile variable is like exiting a synchronized block and reading a volatile variable is like entering a synchronized block.
There is, however, an scenario that is not completely clear for me even after reading the answers to related questions in this site, concretely:
A0
writing to the same volatile variable before the thread A
?In other words: A0
writes the volatile variable, this value is later overwritten by A
(which does not read the variable) and afterwards read by B
, so we have two write operations from different threads (A0
and A
) and a read operation from a third thread (B
).
Can I safely assume that both A
and B
are guaranteed to see everything that was visible to A0
before A0
wrote to the volatile variable?
This is a conceptual question about the Java memory model.
I know that I cannot predict the order in which writes to the volatile variable occurs in threads A0
and A
and the read in thread B
.
However, just for the sake of facilitating the discussion, let's say that A0
starts lots of time before A
does it, and after another significant amount of time B
starts, and let's make the simplifying assumption that this is enough to guarantee that the writes and reads happen in the described order (I know that the order cannot be guaranteed by timing only, this is just a simplification in order to avoid diverging from the original question).
Upvotes: 11
Views: 1062
Reputation: 40256
Can I safely assume that both A and B are guaranteed to see everything that was visible to A0 before A0 wrote to the volatile variable?
No. With volatile stores the happens-before edge is built when there is a predicate involved. For instance, in order for thread A
to see all writes done by A0
's write it needs assert on the volatile write.
int a,b;
volatile c;
A0:
a = 10;
b = 5;
c = 20;
A:
if(c == 20){
// here all writes by A0 are guaranteed to be visible (ie: a and b).
c = 50;
}
If we introduce a third thread B
similar checks would need to be done:
int a,b;
volatile c;
A0:
a = 10;
b = 5;
c = 20;
A:
if(c == 20){
c = 50;
}
B:
if(c == 50){
// B will see all writes done by A and also A0.
}
So the guarantee you are looking for is associated with these happens-before edges defined by a write and subsequent read.
Otherwise, thread A and B can read a
, b
, and c
as either their default values 0 or the values written to.
However, for the sake of facilitating the discussion, lets say that A0 starts lots of time before A does it, and after another significant amount of time B starts.
In practice you probably won't run into memory issues, but you should never write your software with that thought ahead of time.
Also, I doubt you meant it this way, but if you truly mean start - The writes of one thread happens-before the starting of another thread (the start happening within the same thread).
If we remove the read from A
but keep the read from B
in place the possible values are for each thread are
int a,b;
volatile c;
A0:
a = 10;
b = 5;
c = 20;
A:
int l = c; // c could be either 20 or 0
int j = a; // a could be either 10 or 0
int k = b; // b could be either 5 or 0
c = 50;
B:
if(c == 50){
int l = c; // c can be 20 or 50. c cannot be 0
int j = a; // a could be either 10 or 0
int k = b; // b could be either 5 or 0
}
As you can see there is no relationship between the writes of thread A0
and the reads/writes of A
or B
unless we have the read pre-condition.
Upvotes: 0
Reputation: 11662
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). JLS 17.4.4
Thus, the write from A0 synchronizes with the read from B. All earlier writes from A0 happens before the following reads from B. If there are no other writes to these variables, then yes, B must see them.
I can't find that A synchronizes with A0. So, no guarantees on the visibility in A.
From the updated question, we have the following execution, where A0, A1 and B are threads, t0,t1,t2 are statements and writeV,readV are writes/reads to volatile V:
A0 A1 B
t0 <-- writeV
^ t1 <-- writeV
| ^
| \------readV <-- t2
\-----------------------/
Arrows denote happensbefore. We have t0 and t1 happensbefore t2. t2 will see the updates from t0 and t1.
Thus, yes: B is guaranteed to see everything written by A0 in t0. And it will read the volatile value written by A.
But not t0 happensbefore t, nor vice versa. Thus, no: A is not guaranteed to see everything written by A0.
However, this is only conceptual, in real life B can not know if A0 executed at all.
Upvotes: 0
Reputation: 2444
I think the scenario is very simple.
A0 - write to volatile variable V value WA0
A - write to volatile variable V value WA
Now
Can I safely assume that both A and B are guaranteed to see everything that was visible to A0 before A0 wrote to the volatile variable?
if Thread A is only writing to V , then it may or may not see the everything that was visible to A0 before the write to V.
Its only if Thread A reads the variable V and the value it reads turns out to be WA0, only then thread A is guaranteed to see everything visible to A0 before it wrote to V.
The same goes with Thread B it depends which value B sees after reading the value of V.
if B reads the value to V to be WA then it will see all which happened before the write of V in Thread A.
if B reads the value to V to be WA0 then it will see all which happened before the write of V in thread A0.
Also please keep in the mind that
Thread A0
a = 1; // non volatile write
V = WA0; // volatile write
Thread A;
a=3
V = WA; // volatile write
Thread B;
while(V == 'WA') {
assert(a,3); // This may fail
}
You need to understand the code in thread b is incorrectly synchronized volatile gives guarantees related to visibility and reordering, but not of atomicity.
So even if thread B reads value of V to be 'WA' and its guarantee to see everything which happened in thread A before write to V, it doesn't necessarily means that it will see the value of a as 3, because it very well could happened that after reading value of V as WA thread A0 writes to a as 1 which becomes to available to Thread B and thus making your assertion fail. Happens before guarantees you that everything which must happens before a write to v is already happened but its doesn't mean you can't see the future values. You can easily reproduce the scenario by doing something like that
Thread B;
while(V == 'WA') {
Thread.sleep(1000);
assert(a,3); // This may fail
}
Therefore these scenarios Single Writer is preferable or you program should not have code like the above in thread B because then you have data race in your program.
EDIT :
Example Modified:
Thread 1 (T1)
a = 1; // normal write
b = 1; // normal write
v = 1; // volatile write
Thread 2 (T2)
a = 2; // normal write
c = 2; // normal write
v = 2; // volatile write
Thread 3 (T3)
while(true) {
if(v == 2) {
assert (c == 2); // will pass
assert (b == 1); // may fail if T1 hasn't run till
assert (a == 2); // may fail if T1 has run and set the value to 1
break;
}
if(v == 1) {
assert (b == 1); // will pass
assert (c == 2); // may fail if T2 hasn't run till
assert (a == 1); // may fail if T2 has now run setting a == 2
break;
}
}
" for the sake of facilitating the discussion, lets say that A0 starts lots of time before A does it, and after another significant amount of time B starts." as OP states
Now if i restate the above statement as (v == 1 happens before (hb) v == 2 ) .
i.e i assume i have a guarantee that v==1 hb (v==2)
Then behavior of Thread 3 will be changed as
Thread 3 (T3)
while(true) {
if(v == 2) {
assert (c == 2); // will pass
assert (b == 1); // will pass
assert (a == 2); // will pass
break;
}
if(v == 1) {
assert (b == 1); // will pass
assert (c == 2); // may fail if T2 hasn't run till
assert (a == 1); // may fail if T2 has now run setting a == 2
break;
}
}
The problem with the above behavior is how would you guarantee that
v==1 hb (v==2)
I think if you can understand how to establish the above guarantee then you would have answer to your question yourself.
one way is to establish the way @ishrat did in Thread A. There are other ways too in jmm.
but that guarantee can not be achieved by time alone, you need to rely on the underlying guarantees of language spec and platform you are working on.
also please read this excellent article about jmm
Upvotes: 4
Reputation: 533442
Can I safely assume that both A and B are guaranteed to see everything that was visible to A0 before A0 wrote to the volatile variable?
Writing to a volatile doesn't give a thread any happens-before guarantees about reads.
When B
reads, it will see anything A0 could see provided it sees the update it makes. B
can also see anything A
could see provided it sees the value A
wrote. Otherwise B
could see state older than either thread if the value it reads didn't come from that thread i.e. it was too early.
If A0
attempts to write first, it might complete after A
e.g. say A
is on the same socket as the clean copy of the data. It can get access to it before A0
as the later will take longer to acquire the cache line. It is the one which finishes last which determines the eventual state of the data.
Upvotes: 3