I have recently begun to understand how the Java Memory Model works (I don't fully yet).
I now understand that if I have non-final, non-volatile fields in my classes then it is possible for the value of those fields to not be immediately visible to other threads post construction e.g.
public class Example {
Object a;
Object b;
public Example() {
this.a = new Object();
this.b = new Object();
}
}
Example e = new Example();
// now imagine we are in another thread
e.a // could this in theory be null?
e.b // could this in theory be null?
What I'm not sure about is weather if I add a third volatile variable and set it at the end will the writes to a
and b
be guaranteed to be visible to other threads?
public class Example2 {
Object a;
Object b;
volatile Object c;
public Example2() {
this.a = new Object();
this.b = new Object();
this.c = new Object();
}
}
// some where else
Example2 e2 = new Example2();
// are e2.a and e2.b guaranteed to be visible to all other threads? I know (e2.c will be)
This is not something I particularly want to use but I want to understand what would happen in this case.
CodePudding user response:
The constructor runs in a single thread and the instance is not "visible" to other threads until the constructor completes.
So, in this example, a
and b
will only be seen in other threads as null if they are subsequently set to null post-constructor.
The volatile
keyword addresses possible optimizations in which a thread may continue to use an old value for a field after it changes on another thread due to local caching of the value. By adding volatile
to the field, such optimizations are disabled.
By the way, if there's a need to synchronize across threads, using volatile fields is not ideal. Using atomic classes or other constructs from java.util.concurrent
is advisable.
CodePudding user response:
That is a misconception.
Already said is that a constructor is completed on accessing the object.
Not using volatile
means that a thread may have a stale (not updated) value. volatile
ensures that on a thread switch the thread's values of those fields are updated. The reason being that a thread may cache a memory access, and thus run into being out-of-date.
One might think that this is a high-level language specific stupidity, but in fact already machine language knows such things: the ARM processor has a LDEX (load exclusive) and STEX (store exclusive) where one later detects that someone else wrote to the variable first.
I believe it is sufficient to have one volatile
field to keep all fields up-to-date, non-cached. (But that is a technicality.)
For a non-volatile, thus cached value, you could get:
Thread X Thread Y
// a == 13 // a == 13
a *= 2;
// a == 26
// a == 13
a *= 3;
// a == 39
Wait a bit for better experts to give their feedback. The above is just my perception of valatile.