But what about outerVar.innerVar? Does the fact that its parent object ( outerVar ) is marked as volatile make it volatile also?
In this example outerVar.innerVar
will be published correctly, but it will not be volatile. If you were to assign outerVar.innerVar = new Inner()
as some later point you will lose the thread-safe publication.
The rule here is that all write that occur prior to a volatile write are visible after the volatile write. After that write, all normal writes are now thread-unsafe.
So in your example the ordering from a thread would see something similar to
volatile Outer outerVar;
Outer temp = new Outer();
temp.innerVal = new Inner()
outerVar = temp;
Notice the volatile write of outVar = temp
. This is where the synchronization starts. And when another thread reads a non-null outerVar instance, the innerVar field will be safely published.
To reiterate though, any time you assign a new value to outerVar.innerVal
you will lose synchornization. Similarly if innerVal
has any field [after the initial volatile write] writes to those fields will not be correctly synchronized
So to answer your question
Is Java's volatile keyword “recursive” regarding references tree, or must each reference be declared as volatile?
Each field must be declared volatile that will change after the initial volatile write (technically speaking). That being said, you should declare fields either volatile or final if you are sharing among threads.